poj-2411
来源:互联网 发布:淘宝虚假交易 编辑:程序博客网 时间:2024/06/07 20:12
#include <stdio.h>long combineNum[12][2049];long long combineNumLastRow[12][2049];// void getCombineNum() {// for (int i = 1; i <= 11; i++) {// for (int j = 0; j <= 2047; j++) {// if (j > pow(2, i) - 1) {// combineNum[i][j] = 0;// } else if (i == 1) {// combineNum[i][j] = 1;// } else if (i == 2) {// if (j == 0) {// combineNum[i][j] = 2; // 1. -- 2. ||// } else if (j == 1) {// combineNum[i][j] = 1;// } else if (j == 2) {// combineNum[i][j] = 1;// } else if (j == 3) {// combineNum[i][j] = 1;// }// } else {// int first2Bit = j & 0x3;// switch (first2Bit) {// case 0:// combineNum[i][j] = combineNum[i-1][j>>1] + combineNum[i-2][j>>2];// break;// case 1:// combineNum[i][j] = combineNum[i-1][j>>1];// break;// case 2:// combineNum[i][j] = combineNum[i-2][j>>2];// break;// case 3:// combineNum[i][j] = combineNum[i-2][j>>2];// break;// }// }// }// }// }void getCombineNumLastRow() { for (int i = 1; i <= 11; i++) { for (int j = 0; j <= 2047; j++) { if (j > (2<<(i-1)) - 1) { combineNumLastRow[i][j] = 0; } else if (i == 1) { if (j == 0) { combineNumLastRow[i][j] = 0; } else if (j == 1) { combineNumLastRow[i][j] = 1; } } else if (i == 2) { if (j == 0) { combineNumLastRow[i][j] = 1; // 1. -- } else if (j == 1) { combineNumLastRow[i][j] = 0; } else if (j == 2) { combineNumLastRow[i][j] = 0; } else if (j == 3) { combineNumLastRow[i][j] = 1; } } else { int first2Bit = j & 0x3; switch (first2Bit) { case 0: combineNumLastRow[i][j] = combineNumLastRow[i-2][j>>2]; break; case 1: combineNumLastRow[i][j] = combineNumLastRow[i-1][j>>1]; break; case 2: combineNumLastRow[i][j] = 0; break; case 3: combineNumLastRow[i][j] = combineNumLastRow[i-2][j>>2]; break; } } } }}bool checkIfMatch(int upRowStatus, int currentRowStatus, int width, int rowMax) { if ((upRowStatus¤tRowStatus) != 0) { return false; } int tmp = currentRowStatus; int tmp1 = upRowStatus; int zeroNum = 0; int i; for (i = 0; i < width; i++) { char lowBit = 0x00000001 & currentRowStatus; char upperLowBit = 0x00000001 & upRowStatus; if (upperLowBit == 1) { if (lowBit == 1) { return false; } else { if (zeroNum%2 == 0) { zeroNum = 0; } else { return false; } } } else { if (lowBit == 1) { if (zeroNum%2 == 0) { zeroNum = 0; } else { return false; } } else if (lowBit == 0) { zeroNum++; } } currentRowStatus = currentRowStatus>>1; upRowStatus = upRowStatus>>1; } if (i == width) { if (zeroNum%2 != 0) { return false; } } return true;}bool checkIfValidFirstRow(int rowStatus, int width, int rowMax) { int tmp = rowStatus; int zeroNum = 0; int i; for (i = 0; i < width; i++) { char lowBit = 0x00000001 & rowStatus; if (lowBit == 1) { if (zeroNum%2 == 0) { zeroNum = 0; } else { return false; } } else if (lowBit == 0) { zeroNum++; } rowStatus = rowStatus>>1; } if (i == width) { if (zeroNum%2 != 0) { return false; } } return true;}void getCount(int height, int width) { int rowMax = (2<<(width-1)) - 1; long long DP[12][2049] = {0}; if (height == 1) { printf("%lld\n", combineNumLastRow[width][0]); return; } for (int i = height - 1; i >= 1; i--) { for (int j = 0; j <= rowMax; j++) { if (i == height - 1) { DP[i][j] = combineNumLastRow[width][j]; } else { long long sum = 0; int addtime = 0; for (int k = 0; k <= rowMax; k++) { if (checkIfMatch(j, k, width, rowMax)){ // if (j == 1 && i == 1) { // printf("check %d %lld\n", k, DP[i+1][k]); // } sum += DP[i+1][k]; addtime++; } } // if (i == 1) { // printf("j %d addtime %d\n", j, addtime); // } DP[i][j] = sum; } } } long long sum = 0; for (int i = 0; i <= rowMax; i++) { if (checkIfValidFirstRow(i, width, rowMax)) { sum += DP[1][i]; // printf("%lld i %d\n", DP[1][i], i); } } printf("%lld\n", sum);}int main() { getCombineNumLastRow(); while(1) { int height, width; scanf("%d %d", &height, &width); if (height == 0 && width ==0) { return 0; } getCount(height, width); }}
2097ms
憋了几天, 正好工作又忙, 这道题彻底认识到了自己在思维全面性的不足。
用的是常规做法 压缩状态的DP, 压缩状态其实到还不是什么高阶技能, 之前做水题涉及到存储一个小矩形的时候已经用上了,说白了就是编码的本质,
二进制罢了.
关键我最开始根本没有想到以行为单位进行DP, 老想着以一格为单位进行分析, 这样的话, 就算压缩状态也救不了我, 11*11个位, 用128位变量?, 绝对不是这道题的做法。
后来忍不住稍微搜了下, 才发现可以以行为单位分析(唉, 组合数学要补呀, 当时咋就没想到呢, 当然, 用列也可以, 但是本质上是一样的)
某一行的组合状态是一个特定状态, 那么整个长方形的组合状态就是一个特定状态, 不管其他行怎么的排法, 其实相同了很简单, 组合的本质嘛, 某一个特定位置的特定值就可以表示一个整的特定状态(当然, 是从数量上讲).
然后就是该怎么表示了, 很自然的想到, 横排的格子全部为0, 竖排的全部为1, 咋一看很合理, 但是后来发现, 这样的话,根本体现不出来上一行对下一行的影响了。
比如
1100
这一行, 他既可以是 两个向上竖排加上一个横排, 也可以是两个向下竖排将上一个横排, 下一行的状态不能被上一行正确的影响了。后来钻了牛角尖, 甚至想到统计当前行的某一列累计1和0的奇偶性来表示向上和向下横排, 后来也作罢。
后来才想到, 从对下一排的排列有无直接影响作为区分, 如果是横排说这上竖排,那么显然对下一行的对应列没有影响, 那么就可以用0表示, 而下竖排显然影响了下一行对应列(下行对应列不能再放了), 那么就是1.
得到了表示方式, 下一步就是求上一行是这种状态, 下一行能有多少个可能的状态了, 这一步思想也简单,只要这一行为1的列(下竖排)和上一行是1的列(上竖排)之间的空隙都是偶数个0(不然如果是奇数个零,那么必有一个空格没法用长度为2的横排填)就是满足需求的组合, 特殊的是最后一行, 最后一行不能再有下竖排, 那么就必须是上一行上竖排的对应列之间的0数为偶数. 最后一行的每种状态组合数记下来,combineNumLastRow。
这样利用DP
第i行(第一行要特殊处理, 不能有上竖排)某个状态j, D[i][j] 就有这样的规律:
如果只有一行(可以认为上一行对齐没有任何影响, 0这个状态), 那么直接用前面求出最后一行为0的可能组合数就可以了.
如果i是倒数第二行, 那么D[i][j] = combineNumLastRow[width][j], 及最后一行在长度为width的情况下, 上一行为j的情况下的组合数。
对于其他的行i, 这时候, 就体现出空间换时间了, 既可以把此状态对应的下一行可能的状态保存(花费空间), 直接遍历, 也可以直接遍历k: 0 ~ (2<<width-1)-1,
逐个排查(花费时间)是否能适应当前行的状态, 如果能, 那么就加上此下一行状态D[i+1][k]的组合数。
在最后求总的组合数, 也不能简单的把D[1][k](k: 0 ~ (2<<width-1)-1 ) 相加, 要注意,第一行和中间行也是不一样的, 第一行是不能有上竖排的, 因此, 在累加的时候也要做一次筛选(其实这一步应该是可以省去的, 应该有更好的办法,在DP的时候就跳过去)。
最后终于以2097mx AC,和期望的差不多, 必定是耗时的。
看disscuss有神人用了25行搞定, 一定要研究研究。
这道题, 如果位运算用的熟练的话,是用不了那么多代码的. 看样子还只有刷题才能提高呀, 工作这几年,基本是遇不到这些需求的,更多的是一种实现的规划和组织. 不过也没准是屠龙之技?算了,
好歹找工作有用.
- poj 2411
- poj 2411
- poj 2411
- poj 2411
- POJ 2411
- poj-2411
- POJ 2411
- poj 2411
- POJ 2411
- poj 2411 动态规划
- poj 2411 java
- poj 2411 新写法
- poj 2411 (状态压缩)
- HDU 1400 && POJ 2411
- poj 2411 zoj1100
- POJ 2411-状压dp
- POJ 2411 解题报告
- poj 2411| 插头dp
- sqoop的使用
- fluentd结合kibana、elasticsearch实时搜索分析hadoop集群日志
- 真正统治世界的十大算法
- IOS之取乱序数据最大值、最小值方法
- jquery+ajax+ashx
- poj-2411
- Android Push Notification实现信息推送使用
- IOS推送消息(java实现)
- union 与 union all
- android4.0以上屏蔽home键的方法(有源代码)
- cocos2dx 前后端交互
- 关于AjaxFileUpload后台返回Json的处理
- mysql导入与导出
- 【转】域名服务商为DNSPOD如何开启“网站安全防御(WAF)”?