POJ 3254 Corn Fields(DP + 状态压缩)

题目链接: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
",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
",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 }
原文地址:https://www.cnblogs.com/acm-bingzi/p/3300249.html