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行搞定, 一定要研究研究。

这道题, 如果位运算用的熟练的话,是用不了那么多代码的. 看样子还只有刷题才能提高呀, 工作这几年,基本是遇不到这些需求的,更多的是一种实现的规划和组织. 不过也没准是屠龙之技?算了,

好歹找工作有用.

0 0
原创粉丝点击