Vještica
作者:互联网
给定一些串,每个串可以进行重组,最小化这些串最后组成的Trie的结点数。
数据范围指向状压DP。很明显最后的答案和每个串一开始的字符顺序无关,于是可以记录每个串中每个字符的数量。然后发现在两个串合并的时候,为了使得树上结点最少,考虑贪心地把相同的字符排到前面去,于是最后的答案是 \(len_1+len_2-same\)。然后考虑推广,两个集合合并的时候由于两边都会把相同的部分作为每个串的前缀,所以这些串的共同部分是可以作为前缀的,更新即可。
会发现线性更新是错误的,比如下面的数据:
4
dabcrs
eabcrs
ex
ey
先合并后面三个串会使得 e 放在第一层,这样只能节省两个字符;而先合并前面两个串则可以节省后面的五个字符,所以需要枚举子集,用两个子集的答案去更新大的集合。复杂度为 \(O(3^NN)\)。
code
#include<bits/stdc++.h>
//#define feyn
const int N=30;
const int M=1000010;
const int S=1<<16;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int min(int s1,int s2){
return s1<s2?s1:s2;
}
char w[M];
int m,num[N][N];
int f[S],s[N];
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
memset(f,0x3f,sizeof(f));
read(m);
for(int i=1;i<=m;i++){
scanf("%s",w+1);
int len=strlen(w+1);
f[(1<<i-1)]=len;
for(int j=1;j<=len;j++)num[i][w[j]-'a']++;
}
for(int i=1;i<(1<<m);i++){
if(f[i]<M)continue;
memset(s,0x3f,sizeof(s));
for(int j=1;j<=m;j++){
if((i&(1<<j-1))==0)continue;
for(int k=0;k<26;k++)s[k]=min(s[k],num[j][k]);
}
for(int j=i;j;j=(j-1)&i)f[i]=min(f[i],f[j]+f[i-j]);
for(int j=0;j<26;j++)f[i]-=s[j];
}
printf("%d\n",f[(1<<m)-1]+1);
return 0;
}
标签:字符,const,每个,int,合并,wh,tica,Vje 来源: https://www.cnblogs.com/dai-se-can-tian/p/16521977.html