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

   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. 38.Android之ListView简单学习(一)

    android中ListView用的很普遍,今天来学习下,本篇主要以本地数据加载到listview,后面会学习从网络获取数据添加到listview. 首先改下布局文件: <?xml versio ...

  2. POJ1125 Stockbroker Grapevine

    Description Stockbrokers are known to overreact to rumours. You have been contracted to develop a me ...

  3. 洛谷P1202 [USACO1.1]黑色星期五Friday the Thirteenth

    题目描述 13号又是一个星期五.13号在星期五比在其他日子少吗?为了回答这个问题,写一个程序,要求计算每个月的十三号落在周一到周日的次数.给出N年的一个周期,要求计算1900年1月1日至1900+N- ...

  4. 优秀大数据GitHub项目一览

    http://blog.csdn.net/yaoxtao/article/details/50540485 优秀大数据GitHub项目一览 VMware CEO Pat Gelsinger曾说: 数据 ...

  5. 如何通俗地理解 Gradle

    http://www.zhihu.com/question/30432152 一句话概括就是:依赖管理和任务执行. 像Ruby里面的bundler+rake,像iOS中的cocoapods,像node ...

  6. Java初学(五)

    一.成员变量和局部变量区别(成员变量默认为包内访问权限,即使是子类,不在一个包内也无法访问) 1.在类中的位置不同 成员变量:在类中方法外: 局部变量:在方法定义中或者方法声明上 2.在内存中的位置不 ...

  7. mybatis使用小记

    参考资料:http://blog.csdn.net/hupanfeng/article/details/9098453 1.设置不缓存每次查询的结果: 如题,通过设置 flushCache=" ...

  8. wireshark怎么抓包、wireshark抓包详细图文教程

    wireshark怎么抓包.wireshark抓包详细图文教程 作者:佚名  来源:本站整理  发布时间:2013-05-02 19:56:27 本日:53 本周:675 本月:926 总数:3749 ...

  9. WSP (无线会话协议)

    WSP (无线会话协议) WSP是在无线应用协议(WAP:Wireless Application Protocol )组中的协议,用两种服务提供无线应用环境一个稳定的接口. 中文名 WSP WAP ...

  10. hibernate中几个接口作用

    1.Configuration 类 Configuration 类负责管理 Hibernate 的配置信息,包括数据库的URL.用户名.密码.JDBC驱动类,数据库Dialect,数据库连接池等,其加 ...