您的位置:首页 > 其它

【ZJOI2017 Round1练习】D4T2 trie(贪心,状压DP)

2017-03-07 18:55 183 查看

题意:现在 Matej 手上有 N 个英文小写字母组成的单词,

他想知道,如果将这 N 个单词中的字母分别进行重新排列,形成的字母树的节点数最少是多少。

n<=16,len[i]<=1000000

思路:

显然,如果我们希望 Trie 树的节点数尽量少,我们应该先将所有单词公共的字母拿出来,作为 Trie 树最上几层的初始链。

比如说我们有 aaab, baab 和 cab 三个单词,我们会将ab 挑出来,然后剩下的单词就变成了 aa, ab, c。

对于剩下的单词, 我们将其分成两个子集,( aa, ab)和( c),并分别再计算最长的公

共字母链。显然,当集合中有 n 个单词时,有 2^n种方式将这些单词分成两个子集。
由此,我们可以用状态压缩 dp 解决这个问题。一个状态由单词的子集来描述,也就是
说我们有 2^n个状态,并计算每一种子集形成 Trie 树需要的最少节点数,转移时枚举如何将
子集分裂成两个更小的子集,即可解决整个问题。整个算法总的时间复杂度为 O(3^n)。

1 var a:array[1..16,1..26]of longint;
2     dp,f:array[0..200000]of longint;
3     num:array[1..26]of longint;
4     n,i,j,k:longint;
5     ch:ansistring;
6
7 function min(x,y:longint):longint;
8 begin
9  if x<y then exit(x);
10  exit(y);
11 end;
12
13 procedure dfs(k,sta:longint);
14 var i,j:longint;
15 begin
16  if k=n+1 then
17  begin
18   f[sta]:=0;
19   for i:=1 to 26 do num[i]:=1<<25;
20   for i:=1 to n do
21    if sta and (1<<(i-1))>0 then
22     for j:=1 to 26 do num[j]:=min(num[j],a[i,j]);
23   for i:=1 to 26 do f[sta]:=f[sta]+num[i];
24   exit;
25  end;
26  dfs(k+1,(sta<<1)+1);
27  dfs(k+1,sta<<1);
28 end;
29
30 begin
31  assign(input,'trie.in'); reset(input);
32  assign(output,'trie.out'); rewrite(output);
33  readln(n);
34  fillchar(dp,sizeof(dp),$7f);
35  for i:=1 to n do
36  begin
37   readln(ch);
38   k:=length(ch); dp[1<<(i-1)]:=k;
39   for j:=1 to k do inc(a[i,ord(ch[j])-ord('a')+1]);
40  end;
41  dfs(1,0);
42  for i:=1 to (1<<n)-1 do
43  begin
44   j:=i-1;
45   while j>0 do
46   begin
47    dp[i]:=min(dp[i],dp[j]+dp[i xor j]-f[i]);
48    j:=i and (j-1);
49   end;
50  end;
51  writeln(dp[(1<<n)-1]+1);
52  close(input);
53  close(output);
54 end.

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: