poj-3254

22 篇文章 0 订阅
//632K  16MS    C++
#include <cstdio>
#include <cstring>

using namespace std;

const int MAX = 13;

const int M = 100000000;

int map[MAX][MAX];

long long solutionNum;

int W;
int H;
#define S (1<<13)

int degree[13] = {0, 1, 2, 4 ,8,
                    16, 32, 64, 128,
                    256, 512, 1024, 2048};

int DP[13][S];

int rowNumber[13];

char validStatus[13][S];

void init(int i) {
    int j;
    int rowMax = 1<<W;
    for (j = 0; j < rowMax; j++) {
        validStatus[i][j] = 1;

        // check adjacent 1 and impossible plant
        if ((j & (j<<1)) || (~rowNumber[i]) & j) {
            validStatus[i][j] = 0;
        }
    }
}

void beginDP() {
    for (int i = 1; i <= H; i++) {
        init(i);
    }

    int rowMax = 1<<W;
    // printf("rowMax %d\n", rowMax);
    for (int row = 1; row <= H; row++) {
        for (int i = 0; i < rowMax; i++) {
            if (row == 1) {
                if (i == 0) {
                    DP[1][0] = 1;
                } else if (validStatus[row][i]) {
                    DP[1][i] = 1;
                } else {
                    DP[1][i] = 0;
                }
            } else {
                if (!validStatus[row][i]) {
                    DP[row][i] = 0;
                } else {
                    // printf("AAA %d\n", i);
                    for (int j = 0; j < rowMax; j++) {
                        if (validStatus[row-1][j] && (i & j) == 0) {
                            // printf("BBB %d\n", j);
                            DP[row][i] += DP[row-1][j];
                        }
                    }
                    DP[row][i] %= M;
                }
            }
        }
    }

    // for (int i = 1; i <= 2; i++) {
    //     for (int j = 0; j < rowMax; j++) {
    //         printf("%d ", DP[i][j]);
    //     }
    //     printf("\n");
    // }

    solutionNum = 0;
    for (int i = 0; i < rowMax; i++) {
        solutionNum += DP[H][i];
    }
}

void getSolutionNum() {
    beginDP();
    printf("%lld\n", solutionNum%M);
}

int main() {
    scanf("%d %d", &H, &W);
        memset(map, 0, sizeof(map));
        memset(DP, 0, sizeof(DP));
        memset(rowNumber, 0, sizeof(rowNumber));
        for (int y = 1; y <= H; y++) {
            int x = 1;
            for (; x <= W; x++) {
                scanf("%d", &map[x][y]);
                // printf("%d %d %d\n", x, y, map[x][y]);
                rowNumber[y] += map[x][y]*degree[x];
            }
            // printf("%d\n", rowNumber[y]);
        }
        getSolutionNum();
}

妈的,好歹也是经受过2411洗礼的人,这道题我还是给跪了,一开始迫不及待的用了把DFS,不负众望的TLE了.....

后来看了下,才发现跟2411一个套路,都是以行 为递进单位 来进行DP的,也是利用了第n行只能直接影响第n+1行这个特点,

只考虑一行的话,那么就可以进行状态压缩了, 矩形的长最多是12, 一个int(在32位机上应该是32位,更严谨可以用int32_t)将一行的状态(种或者不种)表示出来,

一行的每一个单位格子被这个int数的一位表示(足够,1/0反应 种/不种), 那么又因为最多有12行,一行最多12,DP的数组也最多开到

DP[12][4096]就足够了(其实如果用滚动数组的话,只需要DP[4096],不过我懒得折腾了),对于某个长为L,高为H的矩形,

对于其中一行来说,但从表示其的 int 来说, 一共会有 1<<L 种状态,不过因为题目本身规定了一行并不是所有的格子都可以种庄稼,并且也不能在相邻个各自同时种庄稼,这就意味着这1<<L个状态中,有很多可以直接排除的无效状态:

<1>在本身不能种庄稼的地方种的情况: (~rowNumber[i]) & j, 其中 j 是要检查的状态,rowNumber[i]是该行将将所有能种的地方设为1,不能种的设为0代表的数,(每行的rowNumber在一开始输入的时候就可以求出来)

<2>在相邻格子同时种的情况: j & (j<<1), 一开始没想到这个妙法,还乖乖的一位一位排查。。唉,难道真是智力低下儿童?

上面这个两个状态判断都属于行独立的,及不需要第n-1行的分布情况,就可以判定第n行的这些情况。

所以上面两个可以预处理一下,搞个二维数组保存下。

然后在真正的DP的时候:

<case1> 如果是第一行的话,对于从 0到 1<<L-1的状态 k,只要不是上面两种状态,DP[1][k]就都是1(代表这一种solution)

<case2>如果不是第一行,那么在 0到 1<<L-1的状态k,除了需要不是上面两种情况外, 还要加一个检查:

首先因为第n行的取决与第n-1行的状态,所以这时候需要遍历第n-1行的所有有效状态, 并且对于第n-1行的某个状态j,

还要满足j&k == 0(因为上下相邻的格子也不能同时种庄稼,这样只要k种了, j对应的格子一定要是0), j才是这一行的状态为k时,n-1行可以有的有效状态,

将所有满足k的n-1行的状态的solution数相加就是在第n行,状态为k时,可能的solution数量。


最后,将DP[H][0  <->  1<<L -1]的所有值累加,就是总的solution数量。

以行为单位处理 和  j&(j<<1)这个trick 再mark一下.





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值