【原创】【状态压缩DP】POJ3254 Corn Fields【新手向】

一开始根本不会状压dp,上网各种找题解,但发现他们写的都很......反正我作为一个没有接触过状态压缩的,根本看不懂!

然后看了好多状态压缩的题的题解,总结了一下思路,思路很重要,有了思路转换成计算机语言就好了。因此我先讲一下思路:

  先说说地图,地图上每一行的01代表一个状态,比如输入样例中的111、010,表示第一行的三个位置都可以种稻子,第二行中间的位置可以种稻子,然后,不能种稻子的地方一定不能种稻子(废话...)

可以种稻子的地方可以选择种也可以选择不种,然后有一个前提条件,就是上下左右相邻的地方不能种稻子。

  再说说怎么状态压缩,状态压缩就是把每一个状态压缩成二进制,二进制就是由01组成的,0代表不种,1代表种。二进制就要牵扯到位运算,位运算我就不想说了,百度吧。因此,一串01的二进制数就

可以代表一个状态,例如输入样例第一行是111,那么可以放入第一行的状态有,100、010、001、101、000,因为相邻位置不能放所以只有5种方法,那么第二行就只有2种方法000、010(不考虑其他行)

  那么看第一行和第二行(第一行——第二行),100——000,010——000,001——000,101——000,000——000,这是5种对应方法,还可以100——010,001——010,101——010,000——010这是另外的4种对应方法(第一行5种状态对吧?第二行2种状态,按照乘法原理,应该有5*2 = 10种方法,但是111——010是不合法的,因此样例的答案是10-1 = 9)。

dp[i][j]意思是推到第i行状态为j的方案总数。

那么“100——000”即为dp[2][000]可以由dp[1][100]得到,那么dp[2][000] = dp[2][000] + dp[1][100];

那么“010——000”即为dp[2][000]可以由dp[1][010]得到,那么dp[2][000] = dp[2][000] + dp[1][010];

......

以此类推,逐行递推。

  总结一下思路:先枚举第一行,把所有可能的状态和第一行的地图对比,如果成功,则在循环里继续枚举第二行,把所有可能的状态和第二行的地图对比,如果成功,再和第一行填入的状态对比,如果又匹配成功,则dp[2][000] = dp[2][000] + dp[1][100];方法数加到第二行。这就是一次循环结束了,从新枚举第二行...

把思路转换成代码

can[]代表可行的状态,稍后解释。cur[i]代表地图的第i行 1 for(int i=1;i<m;i++)//枚举每一行
 2 {
 3       for(int j=0;j<tot;j++)//对第i行枚举所有可行的状态j
 4       {
 5              if((can[j]&cur[i])==0)//如果状态j和第i行匹配了
 6              {
 7                   for(int k=0;k<tot;k++)//枚举第i+1行的所有可行的状态k
 8                   {
 9                         if(((can[k]&cur[i+1])==0)&&((can[k]&can[j])==0))//状态k和第i+1行匹配且和状态j匹配
10                             dp[i+1][can[k]] = dp[i+1][can[k]]+dp[i][can[j]];//状态数相加
11                   }
12              }
13       }
14 }

这样核心代码就实现了。

有一个小方法,就是枚举可行状态的时候,假如一行是8列,不必从00000000枚举到11111111,这样很麻烦,所以要预处理。

就是在一开始把,一行的可行状态先求出来就拿“11111111”来说,这肯定是不可能的,因为有相邻的1,所以在一开始就可以舍弃掉。怎么做呢?

假如一行是8列,先从00000000枚举到11111111,对于每一个状态把它左移1位,再和他自己&运算,假如结果>0,就说明有有相邻的1,举个简单的例子:

  01011要判断有没有相邻的1,if(((01011<<1) & (01011)) > 0 )则有相邻的1,(01011<<1) & (01011) 就是 010110和01011按位且运算,这两个红色地方1&1 == 1,因此结果大于0。

怎么实现呢?

1 tot = 0;//全局变量,相当于栈的top,代表可行的状态数
2 for(int i=0;i<(1<<n);i++)//n是列数,i是枚举的状态
3      if((i&(i<<1))==0) can[tot++] = i;

  dp[][]肯定要初始化对吧?不然全是0了,只要对第一行初始化就行了,因为后面的的行都是由第一行得来的

1 for(int i=0;i<tot;i++)
2      if((cur[1]&can[i])==0) dp[1][can[i]] = 1;//和cur[1](第一行)匹配,就给对应的dp赋值为1

  最后一步就是得到cur[]

1 for(int i=1;i<=m;i++)
2 {
3        for(int j=0;j<n;j++)
4        {
5              int num;
6              scanf("%d",&num);
7              if(num==0) cur[i] = (cur[i]|(1<<j));//这里要给0的地方变为1,1的地方放上0,因为要保证不合法的匹配一定是独一无二的。自己思考一下吧
8        }
9 }

最后贴一下完整代码,一开始学的时候,感觉主流代码都一模一样,而且一大堆乱七八糟的函数,麻烦又看不懂,于是下定决心如果自己搞明白了,一定要写一个大家都看得懂的题解,感觉自己讲的比其他都清楚了,如果看不懂就真没办法了......

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define mod 100000000
 5
 6 using namespace std;
 7 int dp[13][1<<12],cur[13];
 8 int can[1<<12],tot,m,n;
 9
10 int main()
11 {
12     while(~scanf("%d%d",&m,&n))
13     {
14         tot = 0;
15         for(int i=0;i<(1<<n);i++)
16             if((i&(i<<1))==0) can[tot++] = i;
17         memset(cur,0,sizeof(cur));
18         memset(dp,0,sizeof(dp));
19         for(int i=1;i<=m;i++)
20         {
21             for(int j=0;j<n;j++)
22             {
23                 int num;
24                 scanf("%d",&num);
25                 if(num==0) cur[i] = (cur[i]|(1<<j));
26             }
27         }
28         for(int i=0;i<tot;i++)
29             if((cur[1]&can[i])==0) dp[1][can[i]] = 1;
30         for(int i=1;i<m;i++)
31         {
32             for(int j=0;j<tot;j++)
33             {
34                 if((can[j]&cur[i])==0)
35                 {
36                     for(int k=0;k<tot;k++)
37                     {
38                         if(((can[k]&cur[i+1])==0)&&((can[k]&can[j])==0))
39                             dp[i+1][can[k]] = dp[i+1][can[k]]+dp[i][can[j]];
40                     }
41                 }
42             }
43         }
44         int ans = 0;
45         for(int i=0;i<tot;i++)
46         {
47             ans += dp[m][can[i]];
48             ans = ans % mod;
49         }
50         printf("%d\n",ans);
51     }
52 }

时间: 2024-10-07 01:29:18

【原创】【状态压缩DP】POJ3254 Corn Fields【新手向】的相关文章

POJ3254 Corn Fields 状态压缩DP

题目大意是在一块M行N列的农场上种谷物,但是不希望彼此相邻(共用一条边),并且有些地方不能种植谷物,给定M,N(范围都不超过12)以及一些不能种谷物的位置,求出一共有多少种方法种谷物. 状态压缩DP,设dp(i, k) 为种到第i行时,第i行状态为k的总共方案数,可以知道dp(i, k) = ∑dp(i -1, k'),其中我们要判断彼此相邻的情况以及不能种植的情况即可. #include <stdio.h> #include <vector> #include <math.

状态压缩dp入门 (poj3254 Corn Fields)

题目链接:http://poj.org/problem?id=3254 题意:给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法. 分析:假如我们知道第 i-1 行的所有的可以放的情况,那么对于第 i 行的可以放的一种情况,我们只要判断它和 i - 1 行的所有情况的能不能满足题目的所有牛不相邻,如果有种中满足,那么对于 i 行的这一中情况有 x 中放法. 前面分析可知满足子状态,我们我们确定可以用dp来解决. 但是我们又发现

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

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

POJ 3254 Corn Fields(状态压缩DP)

Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4739   Accepted: 2506 Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yumm

poj 3254 Corn Fields ,状态压缩DP

题目链接 题意: 一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻.问有多少种放牛方案(一头牛都不放也是一种方案) state[i] 表示对于一行,保证不相邻的方案 状态:dp[i][ state[j] ]  在状态为state[j]时,到第i行符合条件的可以放牛的方案数 状态转移:dp[i][ state[j] ] =Sigma dp[i-1][state'] (state'为符合条

状态压缩dp入门 第一题 POJ 3254 Corn Fields

Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6460   Accepted: 3436 Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yumm

poj 3254 Corn Fields(状态压缩dp)

Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and

POJ 3254 Corn Fields 状态压缩DP

题目链接:http://poj.org/problem?id=3254 思路:状态压缩DP,状态方程为dp[i][j] += (dp[i-1][k]) code: #include <stdio.h> #include <string.h> #define N 500 const int MOD = 100000000; int dp[15][N],ant[N],n,m,k,map[15]; bool ok(int x) { if(x&(x<<1))return

bzoj1725: [Usaco2006 Nov]Corn Fields牧场的安排(状态压缩DP)

本来就是一个写不大来动态规划的人,结果现在又了解到还有种东西叫状态压缩dp,唉... 找了一道例题来试试看:http://www.lydsy.com/JudgeOnline/problem.php?id=1725 解:我们用f[i][j]来表示当前第i行,状态为j的情况下,(且之前的1~i-1的方案数已经确定了),前i行有多少种方案,那么动态转移方程其实很明显: f[i][j]=sum(f[i-1][k]);(当上一行为第k种状态且与当前枚举的第i种状态不会产生相邻的土地,就加上f[i-1][k

poj3254 Corn Fields (状压DP)

http://poj.org/problem?id=3254 Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7588   Accepted: 4050 Description Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parc