狀態壓縮動態規劃:
動態規劃的狀態有時候比較噁心,不容易表示出來,需要用一些編碼技術,把狀態壓縮的用簡單的方式表示出來。
典型方式:當需要表示一個集合有哪些元素時,往往利用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;
}