HDU3247 Resource Archiver(AC自动机+DP)

来源:互联网 发布:最好的搏击软件 编辑:程序博客网 时间:2024/06/05 15:34

HDU3247 Resource Archiver(AC自动机+DP)

http://acm.hdu.edu.cn/showproblem.php?pid=3247

题意:给你n个模板串和m个病毒串,要你设计一个01串,要求该串包含所有的模板串,但是不包含任何一个病毒串,问你这个01串最短长度?

分析:令dp[i][s]=x表示当前在自动机的i节点,并且走过了集合s中的所有模板,且不经过任意病毒串的最小行走距离是x.

dp[i][s]=min(  dp[k][s-{x}]+1 )   k和i都不是病毒串尾节点,且从k能到i节点并且i节点是x号模板.

初值为dp[0][0]=0,其他dp=-1.

最终我们所求为:dp[i][(1<<n)-1] i节点不是病毒节点.

AC自动机要维护match[i]=1表示i节点(或其后缀)是病毒.

还要维护val[i]=s,表示i节点(或其后缀)覆盖了集合s中的模板.

最后递推的时候以s从小到大递推,且要先判断match[i],如果match[i]==0再用当前s|val[i]

但是该题如果这么算的话可能要花9s,其实对于我们有效的节点只是那些value[i]!=0的节点(因为value[i]=0的节点,就算你走上去也不会改变任何的dp值),我们只需要把这些节点分离出来,然后建立一张最短路径图,并且将dp建立在这个图的基础上就可以大大优化计算.dp[i][s]=x表示当前在新图上的i节点,且包含模板的情况为s,且不经过病毒串的需要行走最短距离为x.

注意:虽然模板串最多只有10,但是新图G的节点并不是最多10,可能有超过10个的.但是HDU这题的测试数据中没有出现这种情况,尔可能测试数据比较弱,下面的这个例子输出-1的程序也能过.说明HDU给的那10个模板一般不会出现交叉重叠的情况.

比如下面这个例子:

10 2

00

11

000

111

0000

1111

01000

10111

100000

011111

111111111111111111111

000000000000000000000

答案应该是14,而不是-1.因为串10111110100000是符合要求的串.且由上面的模板生成的G图节点数要超过10个的.

AC代码:62ms

//用自动机中的非病毒模板尾节点建立一张最短路新图G#include<cstdio>#include<cstring>#include<queue>#include<algorithm>using namespace std;const int maxnode=60000+100;const int sigma_size=2;int cnt,pos[110];//pos[i]=j表示新图中i节点在自动机中的节点标号int dis[maxnode];//dis[i]表新图当前节点到i节点的单源最短距离,用于BFS计算单源最短路径int G[110][110];//G[i][j]=x表示新图中有效节点i到j的距离,注意新图的节点可能1int dp[110][1000+100];int N,M;char str[50000+100];int MIN(int a,int b){    if(a<0 || b<0) return a<b? b:a;    else return a<b? a:b;}struct AC_Automata{    int ch[maxnode][sigma_size];    int f[maxnode];    int match[maxnode];//判断是否病毒    int val[maxnode];//包含的单词集合    int sz;    void init()    {        sz=1;        memset(ch[0],0,sizeof(ch[0]));        f[0]=match[0]=val[0]=0;    }    void insert(char *s,int v)    {        int n=strlen(s),u=0;        for(int i=0;i<n;i++)        {            int id=s[i]-'0';            if(ch[u][id]==0)            {                ch[u][id]=sz;                memset(ch[sz],0,sizeof(ch[sz]));                match[sz]=0;                val[sz++]=0;            }            u=ch[u][id];        }        if(v!=-1) val[u] = 1<<v;        else match[u]=1;    }    void getFail()    {        queue<int> q;        for(int i=0;i<sigma_size;i++)        {            int u=ch[0][i];            if(u)            {                f[u]=0;                q.push(u);            }        }        while(!q.empty())        {            int r=q.front();q.pop();            for(int i=0;i<sigma_size;i++)            {                int u=ch[r][i];                if(!u) { ch[r][i]=ch[f[r]][i]; continue; }                q.push(u);                int v=f[r];                while(v && ch[v][i]==0) v=f[v];                f[u]=ch[v][i];                match[u] |= match[f[u]];                val[u] |= val[f[u]];            }        }    }    void path(int k)    {        queue<int> q;        q.push(pos[k]);        memset(dis,-1,sizeof(dis));        dis[pos[k]]=0;        while(!q.empty())        {            int r=q.front();q.pop();            for(int i=0;i<sigma_size;i++)            {                int u=ch[r][i];                if(match[u]) continue;//病毒串                if(dis[u]!=-1)continue;//最短距离已经算出来了                dis[u] = dis[r] +1;                q.push(u);            }        }        for(int i=0;i<cnt;i++)            G[k][i]=dis[pos[i]];    }    int solve()    {        //建立新的最短路图        cnt=1;        pos[0]=0;        for(int i=1;i<sz;i++)if(val[i])            pos[cnt++]=i;        for(int i=0;i<cnt;i++)            path(i);        //DP递推        memset(dp,-1,sizeof(dp));        dp[0][0]=0;        for(int st=0;st<(1<<N);st++)        {            for(int i=0;i<cnt;i++)if(dp[i][st]!=-1)            {                for(int j=0;j<cnt;j++)if(G[i][j]!=-1)                {                    int state= st|val[pos[j]];                    if(dp[j][state]== -1)  dp[j][state] = dp[i][st]+G[i][j];                    else dp[j][state] =min(dp[j][state], dp[i][st]+G[i][j]);                }            }        }        int ans=-1;        for(int i=0;i<cnt;i++)            ans = MIN(ans,dp[i][(1<<N)-1]);        return ans;    }}ac;int main(){    while(scanf("%d%d",&N,&M)==2&&N&&M)    {        ac.init();        for(int i=0;i<N;i++)        {            scanf("%s",str);            ac.insert(str,i);        }        for(int i=0;i<M;i++)        {            scanf("%s",str);            ac.insert(str,-1);        }        ac.getFail();        printf("%d\n",ac.solve());    }}


0 0
原创粉丝点击