北大教你如何高效养牛(误)(点我查看)

   2015-08-21:

  问题的大意就是有一片稻田,里面有很多坑,你要在上面种稻谷,然后呢田里面还会养牛,牛不喜欢扎堆吃饭,所以呢你种的稻谷要间隔种在坑里面,所以一个种了稻谷的坑的上下左右4个坑都不能再种稻谷,而且呢,这些坑有些坑是贫瘠的,不能种稻谷(这不是坑爹吗,难道是有毒?),嗯,大概就是这样,现在那个问你给你一片田问你有多少个种植稻谷的方案

 

                      

  

  这个问题,看上去挺烦的,你又要想着如何不违反规定(种了田上下左右不能种田),还有些地不能种,还要算出最后的方案数,然后你想一下,如果你确定了一个可以种的方案,还不行,或许你和前面可以种的方案冲突,又或者他根本就不是最大的方案数………………………………完了,这题又没法做了

                      

  别急,我们来想办法,我们想着如何简化我们的思路,首先,我们要确立我们思考的方向,一下子考虑全局肯定是不行的了,我们可以把我们的目光放小一点,我们只考虑一行,或者一列这样子(一般做这样的题都必须这样想,包括LIS,LCS,01背包,完全背包统统都是这种思路)

  当然这题肯定是考虑一行比较方便了(你考虑一列也行,但是后面有个操作会很不方便,等一下说),然后对应两种大情况:

  ①左右坑冲突的解决:

  我们可以是根据不能种稻米的坑来确定是否能种植,然后列出所有方案,还可以是先确定所有可行的种植方案,然后再来判断是否和贫瘠坑发生冲突,这两种情况。

  ②上下行冲突的解决:

  上下行也是和左右的冲突解决是一样的,也是比较而已,也是可以像左右那样可以采取两种方案,当然了我们不必上下行都看,我们只用看上一行就可以了,因为我们是从上到下一行一行扫描的,也就是说,所有的本行只和上一行的状态有关,(上上行和上行的关系早就处理好了),如果你想到了这一点,那么这题就完成了一半了

  回过头来我们想一下应该采取哪种方案,很明显,如果我们是先根据坑的情况确定种植的话,那这题的复杂度就大大上升了,为什么?你想啊我们现在不仅是要和本行的左右结构进行冲突的解决,还要对上下结构的冲突进行解决,那么我们起码要保留两个方案的组合,而且每一次方案还要重新构建一遍可行方案,而且还有保存组合数目的问题,实在是太麻烦了,所以我们果断采取先确定所有可行的种植方案,然后再来判断是否和贫瘠坑发生冲突的方法

  做法其实挺简单的,首先我们先把所有可行的状况全部找出来,存在一个地方中,然后每行处理的时候我们一个个拿出来,再和贫瘠坑作对比看有没有冲突,然后然后再和上一行比较看是否冲突,然后我们再来看怎么解决组合的储存问题,因为我们已经储存了所有可行的方案,然后可行的方案又会和所有的可行方案进行比较(即和上一行进行比较),这样一来,我们本行的可行方案数是和可行状态有关,这么看,我们只用把可行方案数“存”在可行状态上就可以啦,然后每次只用与上一行每一个可行的状态累加,存在本行状态上就可以了!多方便。

  讲到这里,这题已经很明了,我们一下子断定,这一题一定是一道DP,而且是一个类型的DP,叫状态压缩,听上去好像有点厉害的样子~

                        

  

  好了思路讲完了,不过先别着急着码代码,我们来解决几个小细节,就是这题比较特殊,因为能不能种只有两个情况,要么种,要么不种,总不能种半棵对吧,那么我们就可以采取0和1来标记坑了(包括贫瘠坑也可以这么标记,当然这题题目已经提示你了),如果是这样,那么我们就可以用二进位来表示这些东西了,这样的话,我们存的状态只用存一些二进位,那么就不用二维数组了,不过这样就要用到位运算,可能会给一开始接触这些题的人带来困惑。(我一开始就是这样,根本看不懂别人写的是什么,用位运算干嘛啊,然后看了半天才明白)。

  为了防止掉坑,我在我自己的代码对位运算加了挺多的注释,一开始看不懂位运算的朋友可以看注释理解理解~

  然后就是一个优化问题,为了方便差不多网上所有的ACMer的代码这题都是用的二维数组,当然你看了我上面的解释就知道这题可以只用两个数组,直接降了差不多一半的内存占用量(当然这题的规模比较小,而且用二维数组实际上是不会多多少内存的,毕竟65536K的内存限制,太宽松了),速度不会差多少

代码如下:(建议复制到IDE去看)

#include <stdio.h>
#include <stdlib.h>
#include <string.h> long long Find(int *, int, int);
void Inivilize_Valid_State(int *,const int,int *);
int if_valid(const int, const int);
void Inivilize_now_and_prev(int **, int **, int); int main()
{
int M, N, i, j, tmp; while (scanf("%d %d", &M, &N) != EOF)//输入行和宽
{
int *S = (int *)malloc(sizeof(int)*(M + ));//因为这是状态01,可以用位运算来表示 for (i = ; i <= M; i++)//输入耕种的图
{
S[i] = ;//首先定义全部为0再说
for (j = ; j <= N; j++)
{
if (scanf("%d", &tmp) &&!tmp)
S[i] |= << (N - j);
//现在是不合法的时候状态为1
//S[i] |= 1 << (N - j)的意思是直到出现一个1,那么我们就把当前的数字往前挪N-j位(把0移走)
//‘|=’就是‘+1’的意思(当然你也可以直接写+1),把1放到最低位
}
}
printf("%I64d", Find(S, M, N));
free(S);
}
} void Inivilize_Valid_State(int *State,const int n,int *top)
{
int i = ;
for (; i < n; i++)
//这里的意思是把区域都划分出来,比如只有三列,那么只有101,010,100,001,这些位置是合法位置,其他都会不行
//一定要注意,0也是一个合法的位置(什么都不种)
if (!(i&(i << )))
State[(*top)++] = i;
} int if_valid(const int valid_state, const int x)
{
//用位运算的方法,如果合法,比较一些二进位,如果有位置同时为1,那么就会返回不合法,否则就是合法位置
if (valid_state&x) return ;
else return ;
} void Inivilize_now_and_prev(int **now, int **prev, int valid_sum)
{
*now = (int *)malloc(sizeof(int)*valid_sum);
*prev = (int *)malloc(sizeof(int)*valid_sum);//两个数组循环交替
memset(*now, , sizeof(int)*valid_sum);
memset(*prev, , sizeof(int)*valid_sum);
} long long Find(int *S, int line, int col)
{
int i, j, sum = , valid_sum = , past;
int *now = NULL, *prev = NULL, *tmp = NULL, *valid_state = NULL;
const int mod = ; valid_state = (int *)malloc(sizeof(int)*( << col));
Inivilize_Valid_State(valid_state, ( << col), &valid_sum);
Inivilize_now_and_prev(&now, &prev, valid_sum); for (j = ; j < valid_sum; j++)//初始化一的情况
{
if (if_valid(valid_state[j], S[]))
prev[j] = ;
} for (i = ; i <= line; i++)
{
for (j = ; j < valid_sum; j++)//遍历合法位置
{
if (if_valid(valid_state[j], S[i]))//如果当前位置不与贫瘠坑发生冲突
{
for (past = ; past < valid_sum; past++)
{
if (if_valid(valid_state[past], S[i - ]) && !(valid_state[j] & valid_state[past]))
//上一个情况没有一个在不可种的位置,且当前位置与下一个位置不冲突
now[j] = (prev[past] + now[j]) % mod;//记得取余
}
}
}
tmp = now; now = prev; prev = tmp;
memset(now, , sizeof(int)*valid_sum);
}
for (i = ; i < valid_sum; i++)//把最后一行所有的组合数加起来
sum = (sum+ prev[i])%mod; free(now); free(prev); free(valid_state);
return sum;
}

PS:最后,说一下我自己的写代码的风格:

  我是很讨厌直接把变量写成a,b,c,d,s……这样的,而且全局变量一大堆,真的很讨厌,但是几乎所有的ACMer都是这样写代码的,思路都是对的,但是写出来就让人看半天。

  而你们看我的代码,我是非常讨厌写全局变量的,所以我宁愿多用几个函数,多传几个指针,我都不想一个全局变量摆在main上面(当然全局变量写上去的确省时间),但是这样我的代码量通常都会比别人多差不多一半。

  对于我自己来说,我来玩ACM是业余爱好(现在我的心态摆正了,ACM是真的是有天赋的人玩的,而我不是),纯粹就是为了巩固自己的算法基础,拿奖什么的就不奢求了,我坚持的是我自己的代码我自己要看的明白,别人也要看的明白(学过编程的),所以我一直奉行一个观念:会写好的变量名,好的函数名,是好程序的开始,比你写一百个注释强多了。以后出来工作都是集体项目,不可能你自己写的代码只有你自己看的懂,别人看要看半天才明白你写的什么,这样的程序员,是失败的。

DP:Corn Fields(POJ 3254)的更多相关文章

  1. poj - 3254 - Corn Fields (状态压缩)

    poj - 3254 - Corn Fields (状态压缩)超详细 参考了 @外出散步 的博客,在此基础上增加了说明 题意: 农夫有一块地,被划分为m行n列大小相等的格子,其中一些格子是可以放牧的( ...

  2. POJ 3254 Corn Fields(状态压缩)

    一道状态压缩的题,错了好多次....应该先把满足的情况预处理出来 #include<iostream> #include<cstdio> #include<cstring ...

  3. DP:Cow Bowling(POJ 3176)

    北大教你怎么打保龄球 题目很简单的,我就不翻译了,简单来说就是储存每一行的总数,类似于状态压缩 #include <stdio.h> #include <stdlib.h> # ...

  4. 水dp第二天(背包有关)

    水dp第二天(背包有关) 标签: dp poj_3624 题意:裸的01背包 注意:这种题要注意两个问题,一个是要看清楚数组要开的范围大小,然后考虑需要空间优化吗,还有事用int还是long long ...

  5. POJ 3254 Corn Fields(状压DP)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 13732   Accepted: 7216 Desc ...

  6. poj3254 Corn Fields (状压DP)

    http://poj.org/problem?id=3254 Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissio ...

  7. P1879 [USACO06NOV]玉米田Corn Fields(状压dp)

    P1879 [USACO06NOV]玉米田Corn Fields 状压dp水题 看到$n,m<=12$,肯定是状压鸭 先筛去所有不合法状态,蓝后用可行的状态跑一次dp就ok了 #include& ...

  8. POJ3254:Corn Fields(状压dp第一发)

    题目:http://poj.org/problem?id=3254 直接上代码吧,刚开始做时主要的问题就是看不懂二进制,有个博客写的太好了,就直接把题解复制在下面了. #include <ios ...

  9. 洛谷P1879 [USACO06NOV]玉米田Corn Fields (状态压缩DP)

    题目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ...

随机推荐

  1. 什么是谷歌loon计划

    互联网服务已经与人类的生活密不可分,但受地理环境限制,目前全球只有三分之一的幸运儿能够体验到这种服务.为了让更多的人感受互联网,Google推出了一项名为“Project Loon”的计划,利用氢气球 ...

  2. 【bzoj1037】 ZJOI2008—生日聚会Party

    http://www.lydsy.com/JudgeOnline/problem.php?id=1037 (题目链接) 题意 有n个boy和m个girl排成一排,求使得任意一段的boy个数girl个数 ...

  3. UVa 437 The Tower of Babylon

    Description   Perhaps you have heard of the legend of the Tower of Babylon. Nowadays many details of ...

  4. javascript显示实时时间

    <html> <script language=Javascript> function time(){ //获得显示时间的div t_div = document.getEl ...

  5. appium-车友会欢迎界面向右滑动4次点击‘立即体验’进入首屏

    代码如下: driver.swipe(610, 2452, 658, 2452, 200) 只是示例滑动1页,可以使用循环,下一页比上一页x坐标大48

  6. mysql语句分析

    explain的每个输出行提供一个表的相关信息,并且每个行包括下面的列: 1,id   select识别符.这是select的查询序列号.2,select_type 可以为一下任何一种类型simple ...

  7. Spring学习8-用MyEclipse搭建SSH框架 Struts Spring Hibernate

    1.new一个web project. 2.右键项目,为项目添加Struts支持. 点击Finish.src目录下多了struts.xml配置文件. 3.使用MyEclipse DataBase Ex ...

  8. WINDOWS渗透与提权总结(2)

    vbs 下载者: 01 1: 02   03 echo Set sGet = createObject("ADODB.Stream") >>c:\windows\cft ...

  9. Java I/O操作

    按字节读取读取文件,并且将文件里面的内容写到另外一个文件里面去 public class CopyBytes { public static void main(String[] args) thro ...

  10. 使用node.js制作简易爬虫

    最近看了些node.js方面的知识,就像拿它来做些什么.因为自己喜欢摄影,经常上蜂鸟网,所以寻思了一下,干脆做个简单的爬虫来扒论坛的帖子. 直接上代码吧. var sys = require(&quo ...