Corn fields poj 3254 (狀壓dp入門)

狀態壓縮動態規劃:
   動態規劃的狀態有時候比較噁心,不容易表示出來,需要用一些編碼技術,把狀態壓縮的用簡單的方式表示出來。
典型方式:當需要表示一個集合有哪些元素時,往往利用2進制用一個整數表示。


再普及一下位運算:

& 按位與      如果兩個相應的二進制位都爲1,則該位的               結果值爲1,否則爲0
| 按位或      兩個相應的二進制位中只要有一個爲1,該位              的結果值爲1
^ 按位異或    若參加運算的兩個二進制位值相同則爲0,               否則爲1
~ 取反        ~是一元運算符,用來對一個二進制數按位取        反,即將0變1,將1變0
<< 左移       用來將一個數的各二進制位全部左移N位,        右補0
>> 右移       將一個數的各二進制位右移N位,移到右端        的低位被捨棄,對於無符號數,高位補0

好吧,回到這題

這道題也就是俗稱的牛喫草問題,【題目大意】一個矩陣裏有很多格子,每個格子有兩種狀態,可以放牧和不可以放牧,可以放牧用1表示,否則用0表示,在這塊牧場放牛,要求兩個相鄰的方格不能同時放牛(不包括斜着的),即牛與牛不能相鄰。問有多少种放牛方案(一頭牛都不放也是一種方案)
【Input】1<=n<=12,1<=m<=12
【Output】一個mod100000000的整數
一看到如此小的數據範圍,又是求方案數,自然而然就聯想到了狀態壓縮的DP。

話不多說,上代碼:

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int dp[20][maxn];
int st[maxn];
int maps[maxn];
int judge1(int x){//這裏原理是判斷當前狀態是否存在兩個1相鄰,根據&的性質,只有兩個都爲1才返回1
	return x&(x<<1);
}
int judge2(int x,int y){//判斷是否可以將這個狀態放到地圖上
	return (maps[x] & st[y]);
}
int main(){
	int i,j,k,m,n;
	int x,y,z;
	while(scanf("%d%d",&m,&n)!=EOF){
		memset(maps,0,sizeof(maps));
		memset(dp,0,sizeof(dp));
		memset(st,0,sizeof(st));
		for(i=1;i<=m;i++){
			for(j=1;j<=n;j++){
				scanf("%d",&x);
				if(!x) maps[i] += (1<<j-1);
			}
		}
		int cnt = 0;
		for(i=0;i<(1<<n);i++){
			if(!judge1(i)){//不停地去除沒用的狀態
				st[++cnt] = i;
			}
		}
		for(i=1;i<=cnt;i++){
			if(!judge2(1,i)){
				dp[1][i] = 1;
			}
		}
		for(i=2;i<=m;i++){
			for(j=1;j<=cnt;j++){
				if(judge2(i,j))continue;//枚舉這一層可能存在的狀態
				for(k=1;k<=cnt;k++){
					if(!judge2(i-1,k)){//枚舉上一層存在的狀態
						if(!(st[k]&st[j]))判斷上下相鄰是否可放
						dp[i][j]+=dp[i-1][k];//狀態轉移
					}
				}
			}
		}
		int ans=0;
		for(i=1;i<=cnt;i++){
			ans=(ans+dp[m][i]%100000000)%100000000;
		}
		printf("%d\n",ans%100000000);
	}
	return 0;
} 


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