POJ3254:Corn Fields(状压dp第一发)
题目:http://poj.org/problem?id=3254
直接上代码吧,刚开始做时主要的问题就是看不懂二进制,有个博客写的太好了,就直接把题解复制在下面了。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define mod 100000000 int m,n,state[],dp[][],top,cur[];
inline bool ok(int x)
{
if(x&x<<) return false;
return true;
}
void init()
{
int total=<<n;
top=;
for(int i=;i<total;i++)
{
if(ok(i)) state[++top]=i;
}
}
inline bool fit(int x,int k)
{
if(cur[k]&x) return false;
return true;
}
int main()
{
int xx;
while(scanf("%d%d",&m,&n)!=EOF)
{
init();
for(int i=;i<=m;i++)
{
cur[i]=;
for(int j=;j<=n;j++)
{
scanf("%d",&xx);
if(xx==) cur[i]+=(<<(n-j));
}
}
memset(dp,,sizeof(dp));
for(int i=;i<=top;i++)
{
if(fit(state[i],))
dp[][i]=;
}
for(int i=;i<=m;i++)
{
for(int k=;k<=top;k++)
{
if(!fit(state[k],i)) continue;
for(int j=;j<=top;j++)
{
if(!fit(state[j],i-)) continue;
if(state[j]&state[k]) continue;
dp[i][k]=(dp[i][k]+dp[i-][j])%mod;
}
}
}
int ans=;
for(int i=;i<=top;i++)
{
ans=(ans+dp[m][i])%mod;
}
printf("%d\n",ans);
}
return ;
}
题目大意:农夫有一块地,被划分为m行n列大小相等的格子,其中一些格子是可以放牧的(用1标记),农夫可以在这些格子里放牛,其他格子则不能放牛(用0标记),并且要求不可以使相邻格子都有牛。现在输入数据给出这块地的大小及可否放牧的情况,求该农夫有多少种放牧方案可以选择(注意:任何格子都不放也是一种选择,不要忘记考虑!
解题思路:以样例数据第一行为例,三个格子都可以放牧,即每个格子都可以选择放,或不放。再考虑附加条件“相邻格子不可同时放牧”,那么我们可以列出单看第一行时的所有可行状态如下(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行)可行状态的编号)
程序代码:
先声明,这道题我是参考了@AcCry的博文 “状态压缩DP总结【POJ3254】【POJ1185】【POJ3311】【HDU3001】【POJ2288】【ZOJ4257】【POJ2411】【HDU3681】”解出的,原文的讲解比较精简,我单独拿出其中第一题详解作为入门。
先帖原帖代码,我在读的过程中加了详细注释,并做了一些细节上的修改:
#include <cstdio>
#include <cstring>
using namespace std; #define mod 100000000
int M,N,top = ;
//top表示每行最多的状态数 int state[],num[];
//state存放每行所有的可行状态(即没有相邻的状态
// int dp[][];
//dp[i][j]:对于前i行数据,每行有前j种可能状态时的解
int cur[];
//cur[i]表示的是第i行整行的情况 inline bool ok(int x){ //判断状态x是否可行
if(x&x<<) return false;//若存在相邻两个格子都为1,则该状态不可行
return true;
}
void init(){ //遍历所有可能的状态
top = ;
int total = << N; //遍历状态的上界
for(int i = ; i < total; ++i){
if(ok(i))state[++top] = i;
}
}
inline bool fit(int x,int k){ //判断状态x 与第k行的实际状态的逆是否有‘重合’
if(x&cur[k])return false; //若有重合,(即x不符合要求)
return true; //若没有,则可行
} int main(){
while(scanf("%d%d",&M,&N)!= EOF){
init();
memset(dp,,sizeof(dp));
for(int i = ; i <= M; ++i){
cur[i] = ;
int num;
for(int j = ; j <= N; ++j){ //输入时就要按位来存储,cur[i]表示的是第i行整行的情况,每次改变该数字的二进制表示的一位
scanf("%d",&num); //表示第i行第j列的情况(0或1)
if(num == ) //若该格为0
cur[i] +=(<<(N-j)); //则将该位置为1(注意要以相反方式存储,即1表示不可放牧
}
}
for(int i = ;i <= top;i++){
if(fit(state[i],)){ //判断所有可能状态与第一行的实际状态的逆是否有重合
dp[][i] = ; //若第1行的状态与第i种可行状态吻合,则dp[1][i]记为1
} } /*
状态转移过程中,dp[i][k] =Sigma dp[i-1][j] (j为符合条件的所有状态)
*/
for(int i = ; i <= M; ++i){ //i索引第2行到第M行
for(int k = ; k <= top; ++k){ //该循环针对所有可能的状态,找出一组与第i行相符的state[k]
if(!fit(state[k],i))continue; //判断是否符合第i行实际情况
for(int j = ; j <= top ;++j){ //找到state[k]后,再找一组与第i-1行符合,且与第i行(state[])不冲突的状态state[j]
if(!fit(state[j],i-))continue; //判断是否符合第i-1行实际情况
if(state[k]&state[j])continue; //判断是否与第i行冲突
dp[i][k] = (dp[i][k] +dp[i-][j])%mod; //若以上皆可通过,则将'j'累加到‘k'上
}
}
}
int ans = ;
for(int i = ; i <= top; ++i){ //累加最后一行所有可能状态的值,即得最终结果!!!泥马写注释累死我了终于写完了!
ans = (ans + dp[M][i])%mod;
}
printf("%d\n",ans);
}
}
POJ3254:Corn Fields(状压dp第一发)的更多相关文章
- 【POJ3254】Corn Fields 状压DP第一次
!!!!!!! 第一次学状压DP,其实就是运用位运算来实现一些比较,挺神奇的.. 为什么要发“!!!”因为!x&y和!(x&y)..感受一下.. #include <iostre ...
- POJ 1684 Corn Fields(状压dp)
描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ ...
- POJ 3254 - Corn Fields - [状压DP水题]
题目链接:http://poj.org/problem?id=3254 Time Limit: 2000MS Memory Limit: 65536K Description Farmer John ...
- POJ 3254 Corn Fields (状压dp)
题目链接:http://poj.org/problem?id=3254 给你n*m的菜地,其中1是可以种菜的,而菜与菜之间不能相邻.问有多少种情况. 状压dp入门题,将可以种菜的状态用一个数的二进制表 ...
- [ An Ac a Day ^_^ ] POJ 3254 Corn Fields 状压dp
题意: 有一块n*m的土地 0代表不肥沃不可以放牛 1代表肥沃可以放牛 且相邻的草地不能同时放牛 问最多有多少种放牛的方法并对1e8取模 思路: 典型的状压dp 能状态压缩 能状态转移 能状态压缩的题 ...
- P1879 [USACO06NOV]玉米田Corn Fields 状压dp/插头dp
正解:状压dp/插头dp 解题报告: 链接! ……我真的太菜了……我以为一个小时前要搞完的题目调错误调了一个小时……90分到100我差不多搞了一个小时…… 然后这题还是做过的……就很气,觉得确实是要搞 ...
- [USACO06NOV]玉米田Corn Fields 状压DP
题面: 农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地.John打算在牧场上的某几格里种上美味的草,供他的 ...
- [USACO06NOV]玉米田Corn Fields (状压$dp$)
题目链接 Solution 状压 \(dp\) . \(f[i][j][k]\) 代表前 \(i\) 列中 , 已经安置 \(j\) 块草皮,且最后一位状态为 \(k\) . 同时多记录一个每一列中的 ...
- [poj3254]Corn Fields_状压dp
Corn Fields poj3254 题目大意:给你一个n*m的地,每一块地可以种或不种,两块种过的地不能挨着,可以一块都不种,问所有的种地方案数. 注释:读入用0和1,1<=n,m<= ...
- Poj - 3254 Corn Fields (状压DP)(入门)
题目链接:https://vjudge.net/contest/224636#problem/G 转载于:https://blog.csdn.net/harrypoirot/article/detai ...
随机推荐
- Javascript特效代码大全(420个)(转)
转载自:Javascript特效代码大全(420个) 收集资料,以便使用+面试+学习 ├ Cookie脚本 ├ 随访问次数变提示 ├ 集成Cookies ├ 使窗口仅弹出一次 ├ 签名提示程序 ├ ...
- php -- PHP5中file_get_contents函数获取带BOM的utf-8文件内容
最近,在用file_get_contents函数来取得文本的内容的时候,出现了一个情况(如下),苦思冥想了n久,不得其解,最后,果然还是得靠百度啊..... 百度到一个解释,下面是原文: PHP5中的 ...
- Yii2中Html的使用
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu4&quo ...
- React-Native 样式指南
https://github.com/doyoe/react-native-stylesheet-guide
- 学习《深入理解C#》—— 数据类型、排序和过滤 (第一章1.1---1.2)
引言 在开始看这本书之前看过一些技术博客,填补自己对于一些知识点的不足.无意中发现了<深入理解C#>这本书,本书主要探讨C# 2.C# 3和C# 4的细节与特性,所以做了一下阅读笔记,欢迎 ...
- kotlin gradle的修改
Kotlin插件包括一个让我们配置Gradle的工具.但是我还是倾向于保持我对Gradle文件读写的控制权,否则它只会变得混乱而不会变得简单.不管怎么样,在使用自动工具之前知道它是怎么工作的是个不错的 ...
- 自定义VIew方法
onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法 onMeasure() 检测View组件及其子组件的大小 onLayout() 当该组件需要分配其子 ...
- win7显示方向旋转快捷键禁用及图形属性打开方法
方法/步骤 1 首先在桌面右键→打开[图形属性],如果没有,请看步骤2.如果有,直接进入步骤3 步骤阅读 2 为了美化桌面右键,往往会把桌面右键中的图形选项隐藏掉,此时,我们可以通过[控制面板]打 ...
- LAMP集群项目四 安装apache、php及其插件
rpm -qa httpd* 查看是否有apache rpm -e httpd-2.2.22.2 卸载该文件,如果不让卸载,则加参数:--nodeps 不做软件中的依赖检查 ./configure ...
- 离线微博工具Open Live Writer(Windows Live Writer)安装过程及server error 500错误解决
必备条件: .net framework 3.5框架(大概是要求3.5或以上,不确定,好像没有人遇到和这个相关的问题) 2017年7月27日最新官方版0.6.2英文离线客户端网盘下载(官网的安装包无法 ...