hdu4057 Rescue the Rabbit,AC自动机,状态压缩dp

hdu4057 Rescue the Rabbit,AC自动机,状态压缩dp

题意:给n(<=10)个串,每个串有个权值,给一个长度(<=100),问这个长度能构造出的最大权值的串是多少。
一个串只能被计算一次权值。

将这些串建trie树,那么树节点不会超过1000个。
然后状态压缩dp,状态有2^10=1024种,节点数1000个,每次转移有四种,共有100次转移,转移用AC自动机优化就是O(1)。
因此复杂度就是O(1000*1024*100*4)。实际会更小一些,因为树节点没有那么多。每次可转移的也没有那么多。

dp[i][j][k]表示走i步,包含j中二进制位为1的串,当前状态是k是否可达。
i那一维可用滚动数组弄掉。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define NN 1040

int tottrie,q[NN],root;
int ptr[500];
char s[NN];

struct trie{
    int fail;
    int son[4];
    int flag;
    void init(){
        fail=0;
        flag=0;
        memset(son,0,sizeof(son));
    }
}tt[NN];

void init(){
    ptr['A']=0;ptr['T']=1;ptr['C']=2;ptr['G']=3;
    tottrie=0;
    root=++tottrie;
    tt[1].init();
}

void insert(char *a,int v){
    int j=root;
    int i;
    while(*a){
        i=ptr[*a];
        if (!tt[j].son[i]) {tt[j].son[i]=++tottrie;tt[tottrie].init();}
        j=tt[j].son[i];
        a++;
    }
    tt[j].flag=1<<v;
}

void build_ac(){
    int i,j,u,v;
    int rear=0,front=0;
    q[++rear]=root;
    while(front<rear){
        u=q[++front];
        for(i=0;i<4;++i){
            j=tt[u].fail;
            while(j&&!tt[j].son[i]) j=tt[j].fail;
            if (tt[u].son[i]){
                v=tt[u].son[i];
                if (!j) tt[v].fail=root;
                else{
                    tt[v].fail=tt[j].son[i];
                    tt[v].flag|=tt[tt[v].fail].flag;
                }
                q[++rear]=v;
            }
            else {
                if (!j) tt[u].son[i]=root;
                else tt[u].son[i]=tt[j].son[i];
            }
        }
    }
}

int v[NN];
int dp[2][NN][NN];
const int inf =10000000;

int main(){
    //freopen("gin.txt","r",stdin);
    int i,j,l,o,pas,now,tn,n,k,ans,vv,tmp;
    while(scanf("%d%d",&n,&k)!=EOF){
        init();
        for(i=1;i<=n;++i){
            scanf("%s%d",s,&v[i]);
            if (strlen(s)>k) continue;
            insert(s,i-1);
        }
        build_ac();
        memset(dp,0,sizeof(dp));
        pas=0;now=1;
        tn=1<<n;
        dp[1][0][1]=1;
        for(i=1;i<=k;++i){
            pas=now;now=1-now;
            for(j=1;j<=tottrie;++j)
                for(l=0;l<tn;++l)
                    {dp[now][l][j]=0;}

            for(j=1;j<=tottrie;++j){
                for(l=0;l<tn;++l)if (dp[pas][l][j]){
                    for(o=0;o<4;++o){
                        vv=tt[j].son[o];
                        dp[now][l|tt[vv].flag][vv]=1;
                    }
                }
            }
        }
        ans=-inf;
        for(i=1;i<=tottrie;++i){
            for(j=0;j<tn;++j)if (dp[now][j][i]){
                tmp=0;
                for(l=0;l<n;++l){
                    if (j&(1<<l)) tmp+=v[l+1];
                }
                if (tmp>ans) ans=tmp;
            }
        }
        if (ans<0) printf("No Rabbit after 2012!\n");
        else printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值