計蒜客-蒜場抽獎(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\n",res);
    
    return 0;    
}
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章