题意:
给n个本源串,m个病毒串
求一个最多的长度的单词包含所有的本源串并不包含任意一个病毒串
串均为01串
思路:
只有10个本源串
一开始想的是直接建立完trie图 然后在图上直接spfa
结果发现 dis[60005][1030] 超内存了
这个时候就要想到
其实只有节点的mark值大于0的节点是我们需要用的
就是那些含有状压权值的节点
那么我们先记录下这些节点是哪些
然后发现其实这些不到100个节点
所以跑100遍spfa 求出两两之间的最短路
然后用这个距离 去状压dp
数组就成了 dp[1030][100] 了
代码:
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"map"
#define inf 0x7fffffff
#include"iostream"
using namespace std;
int triecont;
char bd[60021];
struct trie
{
int mark; //这里如果mark=-1说明不能走 大于0 说明能走到某个单词
int id;
trie *next[3],*fail;
trie()
{
mark=id=0;
memset(next,0,sizeof(next));
fail=NULL;
}
};
trie *root,*node[60002];
void init(char *v,int k,int id)
{
trie *p=root;
for(int i=0; v[i]; i++)
{
int tep=v[i]-'0';
if(p->next[tep]==NULL)
{
p->next[tep]=new trie();
node[triecont]=p->next[tep];
p->next[tep]->id=triecont++;
}
p=p->next[tep];
}
if(k==0) p->mark=(1<<id);
else p->mark=-1;
}
void getac()
{
queue<trie*>q;
q.push(root);
while(!q.empty())
{
trie *p=q.front();
q.pop();
for(int i=0; i<2; i++)
{
if(p->next[i]==NULL)
{
if(p==root) p->next[i]=root;
else p->next[i]=p->fail->next[i];
}
else
{
if(p==root) p->next[i]->fail=root;
else p->next[i]->fail=p->fail->next[i];
q.push(p->next[i]);
if(p!=root)
{
if(p->next[i]->mark==-1 || p->next[i]->fail->mark==-1)
p->next[i]->mark=-1;
else p->next[i]->mark|=p->next[i]->fail->mark;
}
}
}
}
}
struct Node
{
int id;
};
int dis[60001],use[60001];
int useful[222],juli[222][222];
void spfa(int u,int n,int s,int cnt)
{
queue<Node>q;
int i;
for(i=0; i<=n; i++)
{
dis[i]=inf;
use[i]=0;
}
dis[u]=0;
use[u]=1;
Node now;
now.id=u;
q.push(now);
while(!q.empty())
{
Node cur=q.front();
q.pop();
for(i=0; i<2; i++)
{
int v=node[cur.id]->next[i]->id;
if(node[v]->mark==-1) continue;
if(dis[v]>dis[cur.id]+1)
{
dis[v]=dis[cur.id]+1;
now.id=v;
if(!use[now.id])
{
use[now.id]=1;
q.push(now);
}
}
}
}
for(int i=0; i<cnt; i++) juli[s][i]=dis[useful[i]];
}
int dp[1300][222];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m),(n+m))
{
triecont=0;
root=new trie();
node[triecont]=root;
root->id=triecont++;
for(int i=0; i<n; i++)
{
char x[1004];
scanf("%s",x);
init(x,0,i);
}
while(m--)
{
scanf("%s",bd);
init(bd,1,1);
}
getac();
int cnt=0;
useful[cnt++]=0;
for(int i=1; i<triecont; i++) //找出所有我们需要的节点
{
if(node[i]->mark>0) useful[cnt++]=i;
}
for(int i=0; i<cnt; i++) //每个作为起点 跑一遍spfa
{
spfa(useful[i],triecont,i,cnt);
}
for(int i=0; i<cnt; i++) for(int j=0; j<(1<<n); j++) dp[j][i]=inf;
dp[0][0]=0;
for(int i=0; i<(1<<n); i++) //状压dp
{
for(int j=0; j<cnt; j++)
{
if(dp[i][j]==inf) continue;
for(int k=0; k<cnt; k++)
{
int tep=node[useful[k]]->mark|i;
if(j==k) continue;
if(juli[j][k]==inf) continue;
dp[tep][k]=min(dp[tep][k],dp[i][j]+juli[j][k]);
}
}
}
int ans=inf;
for(int i=0; i<cnt; i++) ans=min(ans,dp[(1<<n)-1][i]);
printf("%d\n",ans);
}
return 0;
}