[AC自动机+spfa+状压dp] hdu 3247 Resource Archiver

题意:

给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;
}
时间: 2024-12-21 10:44:11

[AC自动机+spfa+状压dp] hdu 3247 Resource Archiver的相关文章

hdu 3247 Resource Archiver(AC自动机+BFS+DP)

题目链接:hdu 3247 Resource Archiver 题目大意:给定N个需要包含的串,M个不能包含的串,问说满足的最短字符串长度. 解题思路:直接对所有串建立AC自动机,不能满足的串用同一种标记即可.然后处理出所有属于需要包含串的单词节 点,用BFS处理出两两之间的距离,并且过程中是不能经过禁止节点.这样做的原因是节点的个数很多,如果对所有的 节点进行dp的话空间都不够.剩下的就是dp了. #include <cstdio> #include <cstring> #inc

HDU 3247 Resource Archiver (AC自动机 + BFS + 状态压缩DP)

题目链接:Resource Archiver 解析:n个正常的串,m个病毒串,问包含所有正常串(可重叠)且不包含任何病毒串的字符串的最小长度为多少. AC自动机 + bfs + 状态压缩DP 用最短路预处理出状态的转移.可以优化很多 AC代码: #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <queue> using n

【BZOJ-1097】旅游景点atr SPFA + 状压DP

1097: [POI2007]旅游景点atr Time Limit: 30 Sec  Memory Limit: 357 MBSubmit: 1531  Solved: 352[Submit][Status][Discuss] Description FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情.经过这些城市的顺序不是完全随意的,比如说FGD不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶.幸运的是,FGD

BZOJ 1097 POI2007 旅游景点atr SPFA+状压DP

题目大意:给定一张图,要求从第一个点出发,按照某个拓扑序遍历2~k+1的所有节点,然后到达n,求最短路径 首先将所有关键点之间的最短路用SPFA求出来 然后状压DP 令f[state][p]表示已经走过的点集为state,将要走到p点的最短路 记忆化搜索就行了- - 标准卡时过- - #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M

状压DP [HDU 3001] Travelling

Travelling Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4166    Accepted Submission(s): 1339 Problem Description After coding so many days,Mr Acmer wants to have a good rest.So travelling is

状压DP [HDU 1074] Doing Homework

Doing Homework Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5472    Accepted Submission(s): 2311 Problem Description Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lo

[AC自动机+状压dp] hdu 4534 郑厂长系列故事——新闻净化

题意:中文的题目,意思就是说有很多串,每个串都有权值,权值为999的串必须出现,-999的串必须不出现.权值在-999~999之间. 然后必须出现的串不超过8个.然后给一个全为小写目标串,问最少需要删除多少个字母才能够保证必须出现的串都出现,次数一样保证权值最大.输出次数和权值. 然后根据样例,那些必须出现的串,其实权值是0. 思路: 很明显一开始建自动机构成trie图,但是需要注意的就是mark和sum的更新.个人是把所有中间的节点的sum全部赋值成了-inf. 接着只有8个必须出现的串,所以

[AC自动机+状压dp] hdu 2825 Wireless Password

题意: 给n,m,k ,再给出m个单词 问长度为n的字符串,至少在m个单词中含有k个的组成方案有多少种. 思路: 由于m最大是10,所以可以采取状压的思想 首先建立trie图,在每个单词的结束节点标记一个mark=(1<<id),id为单词的编号 然后需要注意的,对于每个节点,应该顺着fail指针遍历一遍, 把所有的mark取一个并集. 因为就是如果单词出现包含的话,比如 she和he 我拿了she,其实等于两个都拿了. dp[i][j][k]  i步在节点j状态k的方案数 然后就是一个四重循

HDU 3247 Resource Archiver (AC自己主动机 + BFS + 状态压缩DP)

题目链接:Resource Archiver 解析:n个正常的串.m个病毒串,问包括全部正常串(可重叠)且不包括不论什么病毒串的字符串的最小长度为多少. AC自己主动机 + bfs + 状态压缩DP 用最短路预处理出状态的转移.能够优化非常多 AC代码: #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <queue> us