hdu 3247 Resource Archiver (ac自动机+BFS+状压dp)

题意:

给出n个资源串,m个病毒串,现在要如何连接资源串使得不含病毒串(可以重叠)。

题解:

真心不会做。

这题注意点好多,稍不留意就wa。

看了题解我是这样理解的,我么可以把资源串和病毒串都放到ac机上,这样的做是为了把病毒串挑掉。在建ac机时,通过判断fail指针对应节点是否是病毒串,然后将fail指针的内容根性到这个节点。同理对于资源串也用相同方法。我们这样设置标记,病毒串ID=-1,资源串ID=对应状态(比如编号是i那么ID=1<<i)。后面建ac机可以整合这些状态。接着还要BFS得到剔除病毒串后可以用资源串到各个资源串的距离,这个方法很巧秒。之后就可以状压了dp[s][i]状态为s,ac机上节点i对应的最小长度。i不一定要在s中,因为可以通过多个部分整合成一个部分。其实建ac机时就将包含关系的资源串整合成了一个串,操作很巧妙的。


#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef long long ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const ll MOD=20090717;
const int maxn=1005;
const int SIZE=60005;
const int alph=2;
char str[maxn];
int dp[B(10)+5][11];
int pos[11];
int dis[SIZE];
int maze[11][11];

struct AC
{
    int next[SIZE][alph],fail[SIZE],ID[SIZE];
    int root,cnt;

    void Init()
    {
        cnt=0;
        root=newNode();
    }

    int newNode()
    {
        for(int i=0;i<alph;i++)
            next[cnt][i]=-1;
        ID[cnt++]=0;
        return cnt-1;
    }

    void Insert(char buff[],int id)
    {
        int now=root;
        int len=strlen(buff);
        for(int i=0,k;i<len;i++)
        {
            k=buff[i]-'0';
            if(next[now][k]==-1)
                next[now][k]=newNode();
            now=next[now][k];
        }
        ID[now]=id;
    }

    void build()
    {
        queue<int>Q;
        fail[root]=root;
        int now=root;
        for(int i=0;i<alph;i++)
        {
            if(next[now][i]==-1)
                next[now][i]=root;
            else
            {
                fail[next[now][i]]=root;
                Q.push(next[now][i]);
            }
        }
        while(!Q.empty())
        {
            now=Q.front();
            Q.pop();
            if(ID[fail[now]]==-1) ID[now]=-1;///和病毒串一样因此要改成-1,这样就相当于删除了病毒串
            else ID[now]|=ID[fail[now]];
            for(int i=0;i<alph;i++)
            {
                if(next[now][i]==-1)
                    next[now][i]=next[fail[now]][i];
                else
                {
                    fail[next[now][i]]=next[fail[now]][i];
                    Q.push(next[now][i]);
                }
            }
        }
    }

    void cmin(int& a,int b)
    {
        if(a==-1) a=b;
        else if(b<a) a=b;
    }

    void bfs(int s,int n)
    {
        queue<int>Q;
        memset(dis,-1,sizeof dis);
        dis[pos[s]]=0;
        Q.push(pos[s]);
        while(!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            for(int i=0;i<2;i++)
            {
                int v=next[u][i];
                if(dis[v]==-1&&ID[v]!=-1)///因此只要处理到邻接的点即可,并不是多个点的路径
                {
                    dis[v]=dis[u]+1;
                    Q.push(v);
                }
            }
        }
        for(int i=0;i<n;i++)
            maze[s][i]=dis[pos[i]];
    }

    int DP(int n)
    {
        int nCnt;
        memset(maze,-1,sizeof maze);
        pos[0]=0;
        nCnt=1;
        for(int i=0;i<cnt;i++) if(ID[i]>0) pos[nCnt++]=i;
        for(int i=0;i<nCnt;i++)
            bfs(i,nCnt);
        memset(dp,0x3f,sizeof dp);
        dp[0][0]=0;
        int full=B(n)-1;
        for(int s=0;s<=full;s++)
        {
            for(int i=0;i<nCnt;i++)
            if(dp[s][i]!=oo)
            {
                //if(!(s&B(i)))continue;
                for(int j=0;j<nCnt;j++)
                if(maze[i][j]!=-1&&i!=j)
                {
                    int st=s|ID[pos[j]];
                    cmin(dp[st][j],dp[s][i]+maze[i][j]);
                }
            }
        }
        int ans=oo;
        for(int i=0;i<nCnt;i++)
            cmin(ans,dp[full][i]);
        return ans;
    }

};
AC ac;

int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)break;
        ac.Init();
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            ac.Insert(str,B(i));
        }
        for(int i=0;i<m;i++)
        {
            scanf("%s",str);
            ac.Insert(str,-1);
        }
        ac.build();
        cout<<ac.DP(n)<<endl;
    }
    return 0;
}
/**
5 2
01000111000001110001
11100000111000110011
00011100011001111011
10001100111101100010
10011110110001001111
10001110000011100011
11000110011110110001

ans=66
*/





你可能感兴趣的:(hdu 3247 Resource Archiver (ac自动机+BFS+状压dp))