poj 3254 Corn Fields 狀壓dp入門題

題目:點擊打開鏈接

題意:一個矩陣裏有很多格子,每個格子有兩種狀態,可以放牧和不可以放牧,可以放牧用1表示,否則用0表示,在這塊牧場放牛,要求兩個相鄰的方格不能同時放牛,即牛與牛不能相鄰。問有多少种放牛方案(一頭牛都不放也是一種方案)

分析:狀壓dp的入門題,這題就是把每一行等效成一個數,由於是由01表示的,所以很容易想到整行用一個二進制來等價,因爲1表示可以選,0表示不可以選,那麼這一行我們可以選哪些呢?很明顯,選的是符合題目要求條件的某些子集,要求的是1,1不可以相鄰,不能選0。那麼我們就可以枚舉每個子集,看看這個子集是否符合要求,這就可以直到這一行有多少可選的狀態了。這只是考慮了這一行的情況,對於下一行滿足的情況,還要考慮豎行是否有1,1相鄰的情況。

具體的實現參考代碼,因爲這幾天要開始系統的學dp,就好好寫一下注釋吧。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=15;
const int INF=0x3f3f3f3f;
const int mod=1e8;
int f[13][1<<13]; //f[i][j],表示到i行,狀態是j,可以選擇則情況數目。
int st[1<<13],cur[13];//st[i]表示符合要求的狀態數,即滿足沒有相鄰的1的二進制。cur[i],表示第i行的狀態的表示
int n,m,num;
void init()
{
    mem(cur,0);
    mem(f,0);
    mem(st,0);
    num=0;
    for(int i=0;i<(1<<m);i++){ //把沒有11相鄰的狀態找出來
        if(!(i&(i<<1)))st[num++]=i;
    }
}
bool judge(int x,int y)
{
    if(x&y)return false;
    return true;
}
int main()
{
    //freopen("f.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        init();
        int t;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                scanf("%d",&t);
                if(t==0){
                    cur[i]+=(1<<j); //把第i行表示出來,爲了以後好判斷是否枚舉的狀態是否是整行狀態的一個自己,01取反表示。這樣在判斷某個狀態是否是子集的
                                    //時候&就可以判斷了。
                }
            }
        }
        for(int i=0;i<num;i++){ //特殊處理第一行,這樣的話
            if(judge(st[i],cur[0])){ 
                f[0][i]=1;
            }
        }
        for(int i=1;i<n;i++){
            for(int j=0;j<num;j++){ 
                if(judge(cur[i],st[j])){ //找出第i行的某個符合條件的子集
                    for(int k=0;k<num;k++){
                        if(judge(cur[i-1],st[k])&& !(st[j]&st[k])) //找出第i-1某個符合條件的子集,並且看看與第i行的子集是否同時選了某個1,(&就可以判斷
                            f[i][j]=(f[i][j]+f[i-1][k])%mod; //對於第i行的每一個子集,第i-1行符合要求的都要枚舉一遍,相加
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<num;i++) //對第n行每個子集的求和
            ans=(ans+f[n-1][i])%mod; 
        printf("%d\n",ans);
    }
    return 0;
}
我這是用二進制子集來表示狀態,子集表示狀態在很多地方還能用到,我記得以前我總結過子集枚舉的情況,可以看看在體會一下。有可能你不能馬上明白,但你遲早會明白的。

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