问题 H: Trie树
时间限制: 1 Sec 内存限制: 128 MB提交: 156 解决: 29
[提交] [状态] [讨论版] [命题人:admin]
题目描述
1.树的每一条边表示字母表中的一个字母
2.树根表示一个空的前缀
3.树上所有其他的节点都表示一个非空前缀,每一个节点表示的前缀为树
根到该节点的路径上所有字母依次连接而成的字符串。
4.一个节点的所有出边(节点到儿子节点的边)中不存在重复的字母。

输入
接下来N行每行一个单词,每个单词都由小写字母组成。
单词的总长度不超过1,000,000。
输出
样例输入
3
a
ab
abc
样例输出
4
思路:状态压缩DP,利用二进制位模拟字符串的集合。小于2^n的每一个数都可以表示一个集合,因为这个数二进制位上的1代表对应的字符串被选择,0代表未被选择。就拿样例来说,(2^3-1:1 2 3 4 5 6 7)其中3的二进制位为11,表示a,ab组成的集合;7的二进制位为111,表示a,ab,abc组成的集合!
对于解法,咱们可以两两结合后再与其他的结合!还要注意这句高亮的代码:
for(int j=i&(i-1);j;j=(j-1)&i)
{
f[i]=min(f[i],f[j]+f[i^j]);
}
遍历子集,对于两个子集(f[j],f[i^j])求和取最小值,好好理解下代码吧!
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int sums=(1<<17),inf=1e9+7,maxn=1000030;
int n,b[20][30],f[sums],len[20],sum,minn;
char a[maxn];
int main()
{
scanf("%d",&n);
for(int i=0;i<=n-1;i++)
{
scanf("%s",a);
len[i]=strlen(a);
for(int j=0;j<len[i];j++)
{
b[i][a[j]-'a']++;
}
}
for(int i=1;i<(1<<n);i++)
{
sum=0;
for(int j=0;j<=n-1;j++)
{
if((1<<j)&i)
{
f[i]+=len[j];
}
}
for(int k=0;k<=25;k++)
{
minn=inf;
for(int j=0;j<=n-1;j++)
{
if((1<<j)&i)
{
minn=min(minn,b[j][k]);
}
}
sum+=minn;
}
for(int j=i&(i-1);j;j=(j-1)&i)
{
f[i]=min(f[i],f[j]+f[i^j]);
}
if(f[i]>sum)
{
f[i]-=sum;
}
}
printf("%d
",f[(1<<n)-1]+1);
}