1. 程式人生 > >狀態壓縮dp入門 (poj3254 Corn Fields)

狀態壓縮dp入門 (poj3254 Corn Fields)

題意:給出一個n行m列的草地,1表示肥沃,0表示貧瘠,現在要把一些牛放在肥沃的草地上,但是要求所有牛不能相鄰,問你有多少种放法。

分析:假如我們知道第 i-1 行的所有的可以放的情況,那麼對於第 i 行的可以放的一種情況,我們只要判斷它和 i - 1 行的所有情況的能不能滿足題目的所有牛不相鄰,如果有種中滿足,那麼對於 i 行的這一中情況有 x 中放法。

前面分析可知滿足子狀態,我們我們確定可以用dp來解決。

但是我們又發現,狀態是一種放法,不是我們平常dp的簡單的狀態,所以要用狀態壓縮!

但是什麼是狀態壓縮呢?

比如前面的情況,一種放法是最多由12個 0 或者 1 組成的,那麼我們很容易想到用二進位制,用二進位制的一個數來表示一種放法。

定義狀態dp【i】【j】,第 i 行狀態為 j 的時候放牛的種數。j 的話我們轉化成二進位制,從低位到高位依次 1 表示放牛0表示沒有放牛,就可以表示一行所有的情況。

那麼轉移方程 dp【i】【j】=sum(dp【i-1】【k】)

狀態壓縮dp關鍵是處理好位運算。

這個題目用到了 & 這個運算子。

用 x & (x<<1)來判斷一個數相鄰兩位是不是同時為1,假如同時為 1 則返回一個值,否則返回 0 ,這樣就能優化掉一些狀態

用 x & y 的布林值來判斷相同為是不是同時為1。

程式碼:

#include <cstdio>
#include <cstring>
const int N = 13;
const int M = 1<<N;
const int mod = 100000000;
int st[M],map[M];  ///分別存每一行的狀態和給出地的狀態
int dp[N][M];  //表示在第i行狀態為j時候可以放牛的種數
bool judge1(int x)  //判斷二進位制有沒有相鄰的1
{
    return (x&(x<<1));
}
bool judge2(int i,int x)
{
    return (map[i]&st[x]);
}
int main()
{
    int n,m,x;
    while(~scanf("%d%d",&n,&m))
    {
        memset(st,0,sizeof(st));
        memset(map,0,sizeof(map));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++){
                scanf("%d",&x);
                if(x==0)
                    map[i]+=(1<<(j-1));
            }

        }
        int k=0;
        for(int i=0;i<(1<<m);i++){
            if(!judge1(i))
                st[k++]=i;
        }
        for(int i=0;i<k;i++)
        {
            if(!judge2(1,i))
                dp[1][i]=1;
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<k;j++)
            {
                if(judge2(i,j))  //判斷第i行 假如按狀態j放牛的話行不行。
                    continue;
                for(int f=0;f<k;f++)
                {
                    if(judge2(i-1,f))   //剪枝 判斷上一行與其狀態是否滿足
                        continue;
                    if(!(st[j]&st[f]))
                        dp[i][j]+=dp[i-1][f];
                }
            }
        }
        int ans=0;
        for(int i=0;i<k;i++){
            ans+=dp[n][i];
            ans%=mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}