hdu3247Resource Archiver (AC自动机+最短路+状压dp)

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 100000/100000 K (Java/Others)
Total Submission(s): 2302    Accepted Submission(s): 724


Problem Description
Great! Your new software is almost finished! The only thing left to do is archiving all your n resource files into a big one.
Wait a minute… you realized that it isn’t as easy as you thought. Think about the virus killers. They’ll find your software suspicious, if your software contains one of the m predefined virus codes. You absolutely don’t want this to happen.
Technically, resource files and virus codes are merely 01 strings. You’ve already convinced yourself that none of the resource strings contain a virus code, but if you make the archive arbitrarily, virus codes can still be found somewhere.
Here comes your task (formally): design a 01 string that contains all your resources (their occurrences can overlap), but none of the virus codes. To make your software smaller in size, the string should be as short as possible.
 

Input
There will be at most 10 test cases, each begins with two integers in a single line: n and m (2 <= n <= 10, 1 <= m <= 1000). The next n lines contain the resources, one in each line. The next m lines contain the virus codes, one in each line. The resources and virus codes are all non-empty 01 strings without spaces inside. Each resource is at most 1000 characters long. The total length of all virus codes is at most 50000. The input ends with n = m = 0.
 

Output
For each test case, print the length of shortest string.
 

Sample Input
   
   
   
   
2 2 1110 0111 101 1001 0 0
 

Sample Output
   
   
   
   
5
 


题意:给你n个01串和m个01串,让你构造一个最短的字符串要包含前n个,但不能包含m中的任意一个。

思路:先把(n+m)个串都放进trie图中,那么n(n<=10)个节点是否已经经过的状态可以用二进制表示出来,然后把m个串的尾节点都设为危险节点,然后进行dp。容易想到的dp是用dp[j][state]表示走到第j个节点,当前n个串的储存情况为state所需要的最少步数,但是这样的时间复杂度是60000*1024*10超时了。所以我们会发现其实在dp过程中,很多节点都是没有用的,即对n个点状态没有影响,只有到n个字符串尾节点对应的trie图节点,state状态才会改变,所以我们考虑先在根节点,n个尾节点在trie图中对应的节点两两之间求最短路即最少步数,然后就在这n个点中状压dp就行了。


#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
#define inf 99999999
#define pi acos(-1.0)
#define maxnode 60050
int dp[20][1030],dist[20][20],vis[maxnode];
int n,m;
map<int,int>jiedianhao;
map<int,int>chuanhao;
map<int,int>::iterator it;

struct trie{
    int sz,root,val[maxnode],next[maxnode][2],fail[maxnode];
    int q[1111111],q1[1111111][2];
    void init(){
        int i;
        sz=root=0;
        val[0]=0;
        for(i=0;i<2;i++){
            next[root][i]=-1;
        }
        jiedianhao[0]=0;
        chuanhao[0]=0;
    }
    int idx(char c){
        return c-'0';
    }
    void charu(char *s,int num){
        int i,j,u=0;
        int len=strlen(s);
        for(i=0;i<len;i++){
            int c=idx(s[i]);
            if(next[u][c]==-1){
                sz++;
                val[sz]=0;
                next[u][c]=sz;
                u=next[u][c];
                for(j=0;j<2;j++){
                    next[u][j]=-1;
                }
            }
            else{
                u=next[u][c];
            }
        }
        if(num==-1)val[u]=-1;
        else{
            jiedianhao[num]=u;
            chuanhao[u]=num;
            val[u]|=(1<<num-1);
        }
    }
    void build(){
        int i,j;
        int front,rear;
        front=1;rear=0;
        for(i=0;i<2;i++){
            if(next[root][i]==-1 ){
                next[root][i]=root;
            }
            else{
                fail[next[root][i] ]=root;
                rear++;
                q[rear]=next[root][i];
            }
        }
        while(front<=rear){
            int x=q[front];
            if(val[fail[x] ]==-1 )val[x]=-1;
            else val[x]|=val[fail[x] ];
            front++;
            for(i=0;i<2;i++){
                if(next[x][i]==-1){
                    next[x][i]=next[fail[x] ][i];

                }
                else{
                    fail[next[x][i] ]=next[fail[x] ][i];
                    rear++;
                    q[rear]=next[x][i];
                }

            }
        }
    }
    void debug(){
        int i,j;
        for(j=0;j<=sz;j++){
            printf("--->%d %d %d %d\n",j,val[j],next[j][0],next[j][1]);
        }
    }
    void bfs()
    {
        int i,j,state,x,t,tt,xx;
        memset(dist,0,sizeof(dist));
        for(i=0;i<=n;i++){
            for(j=0;j<=sz;j++)vis[j]=0;
            vis[jiedianhao[i] ]=1;
            if(i!=0)state=(1<<(i-1) );
            else state=0;
            int front=1;
            int rear=0;
            rear++;
            q1[rear][0]=jiedianhao[i];q1[rear][1]=0;
            while(front<=rear){
                x=q1[front][0];t=q1[front][1];
                front++;
                for(j=0;j<2;j++){
                    xx=next[x][j];
                    tt=t+1;
                    if(vis[xx])continue;
                    if(val[xx]==-1)continue;
                    vis[xx]=1;
                    if(val[xx]!=0){
                        state|=val[xx];
                        dist[i][chuanhao[xx] ]=t+1;
                        if(state==(1<<n)-1)break;
                    }
                    rear++;
                    q1[rear][0]=xx;q1[rear][1]=tt;
                }
                if(state==(1<<n)-1)break;
            }
        }
    }
    void solve(){
        int i,j,state,state1;
        for(i=0;i<=n;i++){
            for(state=0;state<(1<<n);state++ ){
                dp[i][state]=inf;
            }
        }
        dp[0][0]=0;
        for(state=0;state<(1<<n);state++){  //这个状压dp和tsp问题中的有些不同,这里是由已知的状态去推未知的状态,因为可能一个字符串包含其他字符串。
            for(i=0;i<=n;i++){
                if(dp[i][state]!=inf){
                    for(j=1;j<=n;j++){
                        dp[j][state|val[jiedianhao[j] ] ]=min(dp[j][state|val[jiedianhao[j] ] ],dp[i][state ]+dist[i][j] );
                    }
                }
            }
        }
        int minx=inf;
        for(i=0;i<=n;i++){
            minx=min(minx,dp[i][(1<<n)-1]);
        }
        printf("%d\n",minx);
    }
}ac;

char s[1005],str[50050];
int main()
{
    int i,j;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)break;
        jiedianhao.clear();
        chuanhao.clear();
        ac.init();
        for(i=1;i<=n;i++){
            scanf("%s",s);
            ac.charu(s,i);
        }
        for(i=1;i<=m;i++){
            scanf("%s",str);
            ac.charu(str,-1);
        }
        ac.build();
        ac.bfs();
        ac.solve();
    }
    return 0;
}


你可能感兴趣的:(最短路,AC自动机,状压dp)