POJ 3254(狀態壓縮DP)
阿新 • • 發佈:2019-02-04
【題目大意】一個矩陣裡有很多格子,每個格子有兩種狀態,可以放牧和不可以放牧,可以放牧用1表示,否則用0表示,在這塊牧場放牛,要求兩個相鄰的方格不能同時放牛,即牛與牛不能相鄰。問有多少种放牛方案(一頭牛都不放也是一種方案)
【解析】根據題意,把每一行的狀態用二進位制的數表示,0代表不在這塊放牛,1表示在這一塊放牛。首先很容易看到,每一行的狀態要符合牧場的硬體條件,即牛必須放在能放牧的方格上。這樣就能排除一些狀態。另外,牛與牛之間不能相鄰,這樣就要求每一行中不能存在兩個相鄰的1,這樣也能排除很多狀態。然後就是根據上一行的狀態轉移到當前行的狀態的問題了。必須符合不能有兩個1在同一列(兩隻牛也不能豎著相鄰)的條件。這樣也能去掉一些狀態。然後,上一行的所有符合條件的狀態的總的方案數就是當前行該狀態的方案數。
【狀態表示】dp[state][i]:在狀態為state時,到第i行符合條件的可以放牛的方案數
【狀態轉移方程】dp[state][i] =Sigma dp[state'][i-1] (state'為符合條件的所有狀態)
【DP邊界條件】首行放牛的方案數dp[state][1] =1(state符合條件) OR 0 (state不符合條件)不可迷信權威,要善於發現問題的根源,在模仿的前提下自己解決很重要;
#include <cstdio> #include <algorithm> #include <iostream> using namespace std; #define mod 100000000 int A[20][20]; int dp[20][1000],M,N,top = 0; int cpu[20];//代表i行的不能走的情況 int state[1000];//代表不考慮0時所有可以放的情況 void Init() { top = 0; int total = (1 << N) - 1; for(int i = 0;i <= total;i ++) { if(!( i & (i << 1) ) ) { state[++ top] = i;//求出每一行不考慮0時所有可以放的情況 } } } int main() { int i,j,k; cin >> M >> N; for(i = 1;i <= M;i ++) for(j = 1;j <= N;j ++) cin >> A[i][j]; for(i = 1;i <= M;i ++) for(j = 1;j <= N;j ++) if(A[i][j] == 0) cpu[i] += (1 << (j-1));//統計每一行中0的情況 Init(); for(i = 1;i <= top;i ++) { if(!(state[i] & cpu[1])) dp[1][i] = 1; }//對第一行的情況進行初始化 for(i = 2;i <= M;i ++) for(j = 1;j <= top;j ++) { if(!(state[j] & cpu[i]))//先判斷這個狀態是不是可以在本行放 { for(k = 0;k <= top;k ++) if(!(state[j] & state[k]) && !(state[k] & cpu[i-1])) dp[i][j] = (dp[i][j] + dp[i-1][k]) % mod;//如果不合上一行的可放狀態有衝突的話,當前狀態 //就可以有上一行的狀態轉移過來 } } int ans = 0; for(i = 1;i <= top;i ++) ans = (ans + dp[M][i]) % mod; printf("%d\n",ans); return 0; }