HDU4057 Rescue the Rabbit(AC自动机+状压DP)

时间:2023-03-09 06:06:30
HDU4057 Rescue the Rabbit(AC自动机+状压DP)

题目大概是给几个DNA片段以及它们各自的权值,如果一个DNA包含某个片段那么它的价值就加上这个片段的权值,同时包含多个相同DNA片段也只加一次,问长度l的DNA可能的最大价值。

HDU2825大同小异。

  • dp[i][j][S]表示长度i(自动机转移i步)、后缀状态为自动机第j个结点、包含的DNA片段为集合S 的DNA最大价值
  • dp[0][0][0]=0
  • 我为人人转移,从dp[i][j][S]向ATCG四个方向更新dp[i+1][j'][S']
  1. 注意的是要用滚动数组,不然要开上百兆数组。
  2. 用滚动数组要注意初始化。
  3. 转移过程有些计算可以预处理出来,存在数组里,虽然不预处理应该也不会TLE,题目时间给了10000ms。
 #include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define INF (1<<30)
int tn,ch[][],fail[],flag[];
int idx[];
void insert(char *s,int k){
int x=;
for(int i=; s[i]; ++i){
int y=idx[s[i]];
if(ch[x][y]==) ch[x][y]=++tn;
x=ch[x][y];
}
flag[x]|=<<k;
}
void init(){
memset(fail,,sizeof(fail));
queue<int> que;
for(int i=; i<; ++i){
if(ch[][i]) que.push(ch[][i]);
}
while(!que.empty()){
int x=que.front(); que.pop();
for(int i=; i<; ++i){
if(ch[x][i]) que.push(ch[x][i]),fail[ch[x][i]]=ch[fail[x]][i],flag[ch[x][i]]|=flag[ch[fail[x]][i]];
else ch[x][i]=ch[fail[x]][i];
}
}
}
int d[][][<<],VAL[<<];
int main(){
idx['A']=; idx['G']=; idx['T']=; idx['C']=;
int m,n,a;
char str[];
int val[];
while(~scanf("%d%d",&m,&n)){
tn=;
memset(ch,,sizeof(ch));
memset(flag,,sizeof(flag));
for(int i=; i<m; ++i){
scanf("%s%d",str,val+i);
if(strlen(str)>) continue;
insert(str,i);
}
for(int i=; i<(<<m); ++i){
for(int j=; j<m; ++j){
if((i>>j)&) VAL[i]=VAL[i^(<<j)]+val[j];
}
}
init();
for(int j=; j<=tn; ++j){
for(int k=; k<(<<m); ++k) d[][j][k]=-INF;
}
d[][][]=;
for(int i=; i<n; ++i){
int x=i&;
for(int j=; j<=tn; ++j){
for(int k=; k<(<<m); ++k) d[x^][j][k]=-INF;
}
for(int j=; j<=tn; ++j){
for(int k=; k<(<<m); ++k){
if(d[x][j][k]==-INF) continue;
for(int y=; y<; ++y){
d[x^][ch[j][y]][k|flag[ch[j][y]]]=max(d[x^][ch[j][y]][k|flag[ch[j][y]]],d[x][j][k]+VAL[k^(k|flag[ch[j][y]])]);
}
}
}
}
int res=-INF;
for(int i=; i<=tn; ++i){
for(int j=; j<(<<m); ++j) res=max(res,d[n&][i][j]);
}
if(res<) puts("No Rabbit after 2012!");
else printf("%d\n",res);
}
return ;
}