poj-2411

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

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

好歹找工作有用.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值