• 计蒜客-蒜场抽奖(AC自动机+状态压缩DP)


    题解:题意不再说了,题目很清楚的。

    思路:因为N<=10,所以考虑状态压缩 AC自动机中 val[1<<i]: 表示第i个字符串。AC自动机中fail指针是指当前后缀在其他串里面所能匹配的最长前缀的长度,然后我们在这里统计一下以该点结束所能包含的字符串的数量(就是在fail树中该点到根节点所经过的所有为单词结尾的点,在这里我们只要val[x] |= val[fail[x]]就行了,因为val[fail[x]]已经统计过 点fail[x]到根的值了)。

      考虑dp[i][j][k]:表示长度为i,第j个状态点,k为包含的单词的状态,  是否存在。然后转移方程为:if( ch[m-1][j][k] ) ch[m][ ch[j][ char ] ][ k|val[ ch[j][char] ] ]=1;(char : 为第j个状态再往后走一步到达的状态);

      我们的长度是一步一步走的,而且当前步数,仅有上一步确定,所以我们可以压缩步数为奇数偶数,变为:if( ch[ (m-1)&1 ][j][k] ) ch[ m&1 ][ ch[j][ char ] ][ k|val[ ch[j][char] ] ]=1;最后我们只要在第m步的每个状态包含不停字符串状态时的答案里面去最大值就行了。

    参考代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=0x3f3f3f3f;
    const int maxn=1010;
    int ch[maxn][4],val[maxn],fail[maxn];
    int w[12],tot,n,m,Ans[maxn];
    bool dp[2][maxn][1<<11];
    char s[110];
    
    void Init()
    {
        tot=1;
        memset(val,0,sizeof val);
        memset(ch[tot],0,sizeof ch[tot]);    
    }
    
    void Insert(char *s,int x)
    {
        int len=strlen(s),u=0;
        for(int i=0;i<len;++i)
        {
            int c=s[i]-'a';
            if(!ch[u][c]) {ch[u][c]=tot++;memset(ch[tot],0,sizeof ch[tot]);}
            u=ch[u][c];
        }
        val[u]=1<<x;
    }
    
    void GetFail()
    {
        queue<int>q;
        for(int i=0;i<4;++i)
            if(ch[0][i]) q.push(ch[0][i]);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=0;i<4;++i)
            {
                int v=ch[u][i];
                if(!v){ch[u][i]=ch[fail[u]][i];continue;}
                q.push(v);
                fail[v]=ch[fail[u]][i];
                val[v]|=val[fail[v]];
            }
        }
    }
    
    void Work()
    {
        dp[0][0][0]=1;
        for(int i=1;i<=m;++i)
        {
            memset(dp[i&1],0,sizeof dp[i&1]);
            for(int j=0;j<tot;++j)
                for(int k=0;k<4;++k)
                    for(int z=0;z<(1<<n);++z)
                    {
                        if(dp[(i-1)&1][j][z])
                            dp[i&1][ch[j][k]][z|val[ch[j][k]]]=1;    
                    }
        }
    }
    
    int GetAns(int x)
    {
        int ans=0;
        for(int i=0;i<n;++i)
            if(x&(1<<i)) ans+=w[i];
        return ans;    
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        Init();
        for(int i=0;i<n;++i)
            scanf("%s%d",s,w+i),Insert(s,i);
        GetFail();
        Work();
        int res=-INF;
        
        for(int j=0;j<(1<<n);++j) Ans[j]=GetAns(j);
        for(int i=0;i<tot;++i)
            for(int j=0;j<(1<<n);++j)
                if(dp[m&1][i][j]) res=max(res,Ans[j]);
        
        if(res<0) puts("Unhappy!");
        else printf("%d
    ",res);
        
        return 0;    
    }
    View Code
  • 相关阅读:
    《大道至简》第一章 编程的精义
    java课堂练习7
    Java课后练习6
    Java课后练习5
    Java课后练习4
    Java课后练习3
    课堂练习
    求和程序实验报告
    大道至简第二章读后感
    课堂作业例子
  • 原文地址:https://www.cnblogs.com/csushl/p/11459289.html
Copyright © 2020-2023  润新知