HDU - 3247 Resource Archiver(AC自动机+状压dp+bfs)

题目链接:点击查看

题目大意:给出 n 个目标串和 m 个病毒串,要求构造出一个长度最短的,且包含全部 n 个目标串,但是不能包含任意一个病毒串的01字符串,输出其最短长度

题目分析:比较综合的一道题目了,以为是涉及到目标串和病毒串之间的关系,所以读完题目后不难想到先将这些字符串都扔到AC自动机里去,因为 n 非常小,所以可以考虑状态压缩,于是最初的想法就是壮压dp直接转移,dp[ state1 ][ state2 ] 代表着含有目标串状态为state1,且在AC自动机中的状态为state2时的最短长度,但非常遗憾的是,整个dp转移的时间复杂度达到了1024*50000*2,不出意外的话是会TLE的,所以我们必须想办法优化,稍微想一下的话不难发现,AC自动机中绝大部分的字符串都是病毒串的,也就是没有作用的结点,我们真正会用到的也只有那 n<=10 个结点,所以在建好AC自动机后,将可以代表目标串的结点单独拿出,因为之前的转移方程是:(设 i 为目标串的状态,j 为AC自动机的状态,nj 为接下来的状态,id[ i ]为AC自动机内结点 i 所代表的目标串状态)

dp[ i | id[ nj ] ][ nj ] = min( dp[ i | id[ nj ] ][ nj ] , dp[ i ][ j ] + 1 )

而现在的状态转移就变成了:

dp[ i | id[ nj ] ][ nj ] = min( dp[ i | id[ nj ] ][ nj ] , dp[ i ][ j ] + dis[ i ][ nj ] )

其中dis[ i ][ j ]代表着从状态 i 到状态 j 所需要添加的最少字符,到这里,我们就可以发现,因为dis[ i ][ j ]可以用bfs预处理得出,这样总的时间复杂度也就下降为了 1024*10*10 了

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
     
typedef long long LL;
    
typedef unsigned long long ull;
     
const int inf=0x3f3f3f3f;

const int N=1e5+100;
 
char s[N];
 
int fail[N],id[N],trie[N][2],cnt;
 
void insert_word(int _id)
{
	int len=strlen(s);
	int pos=0;
	for(int i=0;i<len;i++)
	{
		int to=s[i]-'0';
		if(!trie[pos][to])
			trie[pos][to]=++cnt;
		pos=trie[pos][to];
	}
	id[pos]=_id;
}
 
void getfail()
{
	queue<int>q;
	for(int i=0;i<2;i++)
	{
		if(trie[0][i])
		{
			fail[trie[0][i]]=0;
			q.push(trie[0][i]);
		}
	}
	while(!q.empty())
	{
		int cur=q.front();
		q.pop();
		if(id[fail[cur]]<0)
			id[cur]=-1;
		else
			id[cur]|=id[fail[cur]];
		for(int i=0;i<2;i++)
		{
			if(trie[cur][i])
			{
				fail[trie[cur][i]]=trie[fail[cur]][i];
				q.push(trie[cur][i]);
			}
			else
				trie[cur][i]=trie[fail[cur]][i];
		}
	}
}

vector<int>state;

int dis[N],maze[15][15],dp[(1<<10)+100][15];

void bfs(int pos)
{
	memset(dis,inf,sizeof(dis));
	queue<int>q;
	q.push(state[pos]);
	dis[state[pos]]=0;
	while(q.size())
	{
		int cur=q.front();
		q.pop();
		for(int i=0;i<2;i++)
		{
			int nj=trie[cur][i];
			if(id[nj]<0)
				continue;
			if(dis[nj]>dis[cur]+1)
			{
				dis[nj]=dis[cur]+1;
				q.push(nj);
			}
		}
	}
	for(int i=0;i<state.size();i++)
		maze[pos][i]=dis[state[i]];
}

void init()
{
	cnt=0;
	memset(id,0,sizeof(id));
	memset(trie,0,sizeof(trie));
	state.clear();
}

int main()
{
#ifndef ONLINE_JUDGE
//    freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
   	int n,m;
   	while(scanf("%d%d",&n,&m)!=EOF&&n+m)
   	{
   		init();
   		for(int i=0;i<n;i++)
   		{
   			scanf("%s",s);
   			insert_word((1<<i));
		}
		while(m--)
		{
			scanf("%s",s);
			insert_word(-1);
		}
		getfail();
		state.push_back(0);
		for(int i=0;i<=cnt;i++)
			if(id[i]>0)
				state.push_back(i);
		for(int i=0;i<state.size();i++)
			bfs(i);
		memset(dp,inf,sizeof(dp));
		dp[0][0]=0;
		for(int i=0;i<(1<<n);i++)
		{
			for(int j=0;j<state.size();j++)
			{
				for(int k=0;k<state.size();k++)
				{
					if(dp[i][j]!=inf)
					{
						if(maze[j][k]==inf)
							continue;
						if(j==k)
							continue;
						dp[i|id[state[k]]][k]=min(dp[i|id[state[k]]][k],dp[i][j]+maze[j][k]);
					}
				}
			}
		}
		int ans=inf;
		for(int i=0;i<state.size();i++)
			ans=min(ans,dp[(1<<n)-1][i]);
		printf("%d\n",ans);	
	}
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    return 0;
}
发布了655 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/104432211
今日推荐