题目链接:http://poj.org/problem?id=3254
题目大意:Farmer John 放牧cow,有些草地上的草是不能吃的,用0表示,然后规定两头牛不能相邻放牧。问你有多少种放牧方法。
Sample Input
2 3
1 1 1
0 1 0
Sample Output
9
分析:对于这种二维地图型,一般设状态dp[i][j]表示第 i 行第 j 状态达到要求的总数
输入地图,用map[i]表示第 i 行中的状态。为了是sta[k]表示可行状态更加方便,map[i]中用0表示可放牧,1表示不可放牧,这样如果(sta[k]&map[i]==0)则说明满足放牧要求。
动态规划:初始化:令dp[0][j]中可以在第一行放牧的状态j,dp[0][j]=1;
转移方程:dp[i][j] = dp[i][j] + dp[i-1][k],k为所有满足条件的状态
代码如下:
1 # include<stdio.h> 2 # include<string.h> 3 const int MAXN = 1<<12; 4 const int MOD = 100000000; 5 int sta[MAXN],num; 6 int dp[13][MAXN]; //dp[i][j]表示第i行在集合j中满足条件的方案数 7 int map[13]; //表示输入中每一行的状态 8 int n,m; 9 void init(){ 10 num =0; 11 int sum = 1<<m; 12 for(int i=0; i<sum;i++) //从1到sum里边有满足该状态中放牛的位置不相邻的方案有num个 13 if(i&(i<<1)) 14 continue; 15 else 16 sta[num++] = i; 17 } 18 19 void DP(){ 20 int i,j,k; 21 for(i=0; i<num; i++) 22 if(!(sta[i]&map[0])) //寻找第一层满足条件的方案,令它为1 23 dp[0][i] = 1; 24 for(i=1; i<n; i++) 25 for(j=0; j<num; j++) //第i行是状态j 26 if(sta[j]&map[i]) //去掉与草场矛盾 27 continue; 28 else 29 for(k=0; k<num; k++){ //第i-1行是状态k 30 if(map[i-1]&sta[k]) //去掉与草场矛盾的 31 continue; 32 if(sta[k]&sta[j]) //去掉与上一行状态矛盾的 33 continue; 34 dp[i][j] = (dp[i][j] + dp[i-1][k]) % MOD; 35 } 36 int ans = 0; 37 for(i=0; i<num; i++) 38 ans = (ans+dp[n-1][i])%MOD; 39 printf("%d\n",ans); 40 } 41 int main(){ 42 int i,j,temp; 43 while(scanf("%d%d",&n,&m)!=EOF){ 44 memset(map,0,sizeof(map)); 45 for(i=0; i<n; i++) 46 for(j=0; j<m; j++){ 47 scanf("%d",&temp); 48 if(temp==0) //将每行状态二进制表示,1代表题目中的0,代表1 49 map[i] += 1<<(m-j-1); 50 } 51 init(); 52 DP(); 53 } 54 return 0; 55 }
也可以使用滚动数组:
1 # include<stdio.h> 2 # include<string.h> 3 const int MAXN = 1<<12; 4 const int MOD = 100000000; 5 int sta[MAXN],num; 6 int dp[2][MAXN]; //dp[i][j]表示第i行在集合j中满足条件的方案数 7 int map[13]; //表示输入中每一行的状态 8 int n,m; 9 void init(){ 10 num =0; 11 int sum = 1<<m; 12 for(int i=0; i<sum;i++) //从1到sum里边有满足该状态中放牛的位置不相邻的方案有num个 13 if(i&(i<<1)) 14 continue; 15 else 16 sta[num++] = i; 17 } 18 19 void DP(){ 20 int i,j,k; 21 memset(dp,0,sizeof(dp)); 22 for(i=0; i<num; i++) 23 if(!(sta[i]&map[0])) //寻找第一层满足条件的方案,令它为1 24 dp[0][i] = 1; 25 for(i=1; i<n; i++){ 26 for(j=0; j<num; j++) { //第i行是状态j 27 dp[i%2][j] = 0; //用来初始化,在滚动数组里边很重要 28 if(sta[j]&map[i]) //去掉与草场矛盾 29 continue; 30 else 31 for(k=0; k<num; k++){ //第i-1行是状态k 32 if(map[i-1]&sta[k]) //去掉与草场矛盾的 33 continue; 34 if(sta[k]&sta[j]) //去掉与上一行状态矛盾的 35 continue; 36 dp[i%2][j] = (dp[i%2][j] + dp[(i+1)%2][k]) % MOD; 37 } 38 } 39 } 40 int ans = 0; 41 int temp = (n+1)%2; 42 for(i=0; i<num; i++) 43 ans = (ans+dp[temp][i])%MOD; 44 printf("%d\n",ans); 45 } 46 int main(){ 47 int i,j,temp; 48 while(scanf("%d%d",&n,&m)!=EOF){ 49 memset(map,0,sizeof(map)); 50 for(i=0; i<n; i++) 51 for(j=0; j<m; j++){ 52 scanf("%d",&temp); 53 if(temp==0) //将每行状态二进制表示,1代表题目中的0,代表1 54 map[i] += 1<<(m-j-1); 55 } 56 init(); 57 DP(); 58 } 59 return 0; 60 }