[BZOJ 1879][SDOI 2009]Bill的挑戰 題解(狀壓DP)

[BZOJ 1879][SDOI 2009]Bill的挑戰

Description

Solution

1.考慮狀壓的方式。ios

方案1:若是咱們把每個字符串壓起來,用一個布爾數組表示與每個字母的匹配關係,那麼空間爲26^50,爆內存;數組

方案2:把每個串壓起來,多開一維記錄匹配字符,那麼空間爲nlen26,合法,但不便於狀態的設計和轉移;spa

方案3:把每個串同一個位置的字符放在一塊兒,用一個布爾數組記錄與每個小寫字母的匹配關係,那麼空間爲26^15*len,爆內存;設計

方案4:把每個串同一個位置的字符壓起來,用多開一維的整形數組記錄與每個小寫字母的匹配關係,空間爲2^15*len,合法;code

採用方案4,那麼關係具體的記錄方式就是,開一個壓縮數組r[2^15][len],r[i][j]表示全部串第i位與第j個小寫字母的匹配狀況:blog

例如,第1到n個串的第i位分別爲:?,a, b,c,那麼他們與'a'的匹配狀況爲r[i][0]=0011;ip

void init(){
    for(R int i=0;i<len;++i)//第i列 
        for(R int x=0;x<26;++x){//對'a'增量爲x 
            r[i][x]=0;
            for(R int k=0;k<n;++k)//第k個串 
                if(s[k][i]=='?'||s[k][i]=='a'+x)r[i][x]|=(1<<k);
        }
}

2.考慮DP的過程內存

方案1:f[i][j]表示當前匹配長度位i,狀態爲j,實際上也就是符合要求的匹配狀況爲j時的方案數ci

方案2:f[i][j]表示當前匹配到第i位,狀態爲j時的方案數;字符串

兩種方案都可,只是第一種的i永遠比第二種的i多1罷了。

可是咱們要採用方案1,由於初始化時,未匹配狀況應該只有一個那就是111...111,因此對於第一種狀況初始化就很是簡單,也就是f[0][(1<<n)-1]=1;

而後就是轉移,咱們發現當且僅當前一位時當前狀態合法纔可轉移,咱們轉移採用擴展狀態的方式,即在當前狀態上枚舉增長狀態,因此狀態轉移方程是:

(f[i][j&r[i-1][x]]+=f[i-1][j])%=mod;

Code

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define R register
typedef long long ll;
using namespace std;

const int mod=1e6+3;

string s[20]; 
int n,m,r[51][50010],f[51][50010];

inline int lowbit(int x){return x&-x;}

void init(){
    cin>>n>>m;
    memset(f,0,sizeof(f));
    for(R int i=0;i<n;++i)cin>>s[i];
    int len=s[0].size();
    for(R int i=0;i<len;++i)//第i列 
        for(R int x=0;x<26;++x){//對'a'增量爲x 
            r[i][x]=0;
            for(R int k=0;k<n;++k)//第k個串 
                if(s[k][i]=='?'||s[k][i]=='a'+x)r[i][x]|=(1<<k);
        }
}

void work(){
    int lim=(1<<n)-1;
    int len=s[0].size();
    f[0][lim]=1;
    for(R int i=1;i<=len;++i)
        for(R int j=0;j<=lim;++j)//狀態 
            if(f[i-1][j])//若是當前前一列子集被更新過,即要擴展的狀態合法 
                for(R int x=0;x<26;++x)//對'a'增量爲x
                    (f[i][j&r[i-1][x]]+=f[i-1][j])%=mod; 
    int ans=0;
    for(R int i=0;i<=lim;++i){
        int temp=i,cnt=0;
        while(temp){cnt++;temp-=lowbit(temp);}
        if(cnt==m) (ans+=f[len][i])%=mod;
    }
    printf("%d\n",ans);
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        init();
        work();
    }
    return 0;
}
相關文章
相關標籤/搜索