BZOJ1725】[Usaco2006 Nov]Corn Fields牧場的安排 狀壓DP
Description
Farmer John新買了一塊長方形的牧場,這塊牧場被劃分成M列N行(1<=M<=12; 1<=N<=12),每一格都是一塊正方形的土地。FJ打算在牧場上的某幾格土地裏種上美味的草,供他的奶牛們享用。遺憾的是,有些土地相當的貧瘠,不能用來放牧。並且,奶牛們喜歡獨占一塊草地的感覺,於是FJ不會選擇兩塊相鄰的土地,也就是說,沒有哪兩塊草地有公共邊。當然,FJ還沒有決定在哪些土地上種草。 作為一個好奇的農場主,FJ想知道,如果不考慮草地的總塊數,那麽,一共有多少種種植方案可供他選擇。當然,把新的牧場荒廢,不在任何土地上種草,也算一種方案。請你幫FJ算一下這個總方案數。
Input
* 第1行: 兩個正整數M和N,用空格隔開
* 第2..M+1行: 每行包含N個用空格隔開的整數,描述了每塊土地的狀態。輸入的第i+1行描述了第i行的土地。所有整數均為0或1,是1的話,表示這塊土地足夠肥沃,0則表示這塊地上不適合種草
Output
* 第1行: 輸出一個整數,即牧場分配總方案數除以100,000,000的余數
Sample Input
2 31 1 1
0 1 0
Sample Output
9解題思路 :以樣例數據第一行為例,三個格子都可以放牧,即每個格子都可以選擇放,或不放。再考慮附加條件“相鄰格子不可同時放牧”,那麽我們可以列出單看第一行時的所有可行狀態如下(1代表放牧,0代表不放牧)
編號 | 狀態 |
1 | 0 0 0 |
2 | 0 0 1 |
3 | 0 1 0 |
4 | 1 0 0 |
5 | 1 0 1 |
由此,可將表中的狀態看作二進制表示,那麽,只需將每種狀態轉化為相應的十進制數,即可只用一個數字,就能表示某一種狀態,如下表:
編號 | 二進制 | 十進制 |
1 | 0 0 0 | 0 |
2 | 0 0 1 | 1 |
3 | 0 1 0 | 2 |
4 | 1 0 0 | 4 |
5 | 1 0 1 | 5 |
至此我們看到,在只考慮第一行的時候,有5種可行的放牧方案,但這只是我們要做的第一步。接下來要將第二行納入考慮:
首先思考:納入第二行後,會對當前問題造成什麽樣的影響?
答案還是那句話:“ 相鄰格子不可同時放牧 ”!
也就是說,不止左右相鄰不可以,上下之間也不能存在相鄰的情況。
首先觀察第二行,只有中間的格子可以放牧,那麽我們的狀態表格就可以相對簡單些了~如下:
編號 | 二進制 | 十進制 |
1 | 0 0 0 | 0 |
2 | 0 1 0 | 2 |
1、當第二行的狀態為編號1時,第二行的三個格子都沒有放牧,那麽就不會與第一行的任何情況有沖突,第一行的5種方案都可行,即:第二行選用編號1的狀態時,結合第一行,可得到5種可行的放牧方案;
2、當第二行的狀態為編號2時,第二行中間的格子已經放牧了,那麽第一行中間的格子就不可以放牧。看表2,發現其中第3種狀態與當前第二行沖突,那麽第一行只有4種方案是可行的,即:第二行選用編號2的狀態時,結合第一行,可得到4種可行的放牧方案;
那麽,在樣例數據給出的情況下,我們的最終答案即為5+4=9;
通過對樣例數據的分析即可以發現不同狀態之間的關系:
以 dp[i][state(j)] 來表示對於 前i行 , 第i行 采用 第j種狀態 時可以得到的 可行方案總數!
例如:回頭看樣例數據,dp[2][1]即代表第二行使用第2中狀態(0 1 0)時可得的方案數,即為4;
那麽,可得出狀態轉移方程為:
dp[i][state(j)]=dp[i-1][state(k1)]+dp[i-1][state(k2)]+......+dp[i-1][state(kn)] (kn即為上一行可行狀態的編號,上一行共有n種可行狀態)
最終ans=dp[m][state(k1)]+dp[m][state(k2)]+......+dp[m][state(kn)]; (kn即為 最後一 行 (第m行) 可行狀態的編號)
AC代碼:
#include<stdio.h> #include<string.h> const int mod = 100000000; int n,m,tot,v[20],ans;//v[i]//第i行整行的情況 int dp[20][520],s[520];//dp對於前i行,每行有前j種可能狀態時的解 //s[i]存儲每行所有可行的狀態 int main( ) { int a; while(scanf("%d%d",&n,&m)!=EOF) { tot=ans=0; memset(dp,0,sizeof(dp)); memset(s,0,sizeof(s)); memset(v,0,sizeof(v)); for(int i=0 ; i<1<<m ; i++) if((i&(i<<1))==0)///記錄不相鄰的狀態 s[++tot]=i; for(int i=1 ; i<=n ; i++) { for(int j=1 ; j<=m ; j++) { scanf("%d",&a); if(a==0) v[i]+=1<<j-1;//相反方式存儲 } } dp[0][1]=1; for(int i=1 ; i<=n ; i++) { for(int j=1 ; j<=tot ; j++)//判斷第i行 假如按狀態j放牛的話行不行 { if(s[j]&v[i])//剪枝 判斷上一行與其狀態是否滿足 continue; for(int k=1 ; k<=tot ; k++) { if(s[j]&s[k]) continue; dp[i][j]=(dp[i][j]+dp[i-1][k])%mod; } } } for(int i=1;i<=tot;i++) { if(s[i]&v[n]) continue; ans=(ans+dp[n][i])%mod; } printf("%d\n",ans); } return 0; }View Code
BZOJ1725】[Usaco2006 Nov]Corn Fields牧場的安排 狀壓DP