zoukankan      html  css  js  c++  java
  • 骨牌摆放问题 POJ 2411(状态压缩DP)

    题目:

    给你(n*m(1<=n,m<=11))的方格矩阵,要求用1*2的多米诺骨牌去填充,问有多少种填充方法。

    比如下图是对于如下2x6的方格矩阵,可能的填充方案之一。

    该如何使用动态规划的方式解决这道题呢?先了解一下状态压缩算法。

    状态压缩通常是使用一个整数来表示一个集合,比如整数3,二进制表示为11,第一位状态为1,第二位状态为1,数字2的二进制表示为10,第一位的状态为1,第二位的状态为0,数字1的二进制表示为01,第一位为0,第二位为1,数字0的二进制可以表示为00,两位的状态都是0。

    这就是说,状态可以保存在一个整数里面,对于状态压缩DP,其实也是用状态压缩后的整数表示一个维度,然后进行状态转移。

    状态压缩DP:采用状态压缩算法的DP问题,也就是用整数表示集合,然后将该整数作为DP的一个维度来进行DP状态转移。

    继续看题,其实这道题的解法并不简单,甚至于有些晦涩,光是理解状态压缩DP并不一定能够看懂源代码。

    首先定义状态转移方程:

    (f(i, j) = f(i-1, k))之和,(j, k)属于([0,1<<m))

    且满足 (j&k==0)

    满足 $ j | k$是有连续个0的状态

    f(i,j)表示第i行的状态为j时,前i行的方案总数。且定义状态1表示竖着的上面一部分,其他状态为0。

    对于约束的理解是本题的关键,

    约束1:j&k == 0 是因为不可能上下都是1。

    约束2:j|k合并后状态0必须是连续偶数个,这个可以预先缓存起来,不用每次都算。详见代码注释。

    源代码:

    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <algorithm>
    #include <bitset>
    #include <iostream>
    #include <set>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    long long f[12][1 << 11];
    int in_s[1 << 11];
    
    // 注意这里一定要用64位的整数
    long long solve(int n, int m) {
        for (int i = 0; i < 1 << m; ++i) {
            int cnt = 0, has_odd = 0;
            // 遍历m中的每一位
            for (int j = 0; j < m; ++j) {
                if (i >> j & 1) {
                    //把cnt的值先存放到has_odd上,然后清零
                    has_odd |= cnt, cnt = 0;
                } else {
                    //如果是偶数个0,则肯定cnt最后为0,因为0^1=1 1^1=0
                    cnt ^= 1;
                }
            }
            // 如果cnt=1则表明存在连续的奇数个0位
            in_s[i] = has_odd | cnt ? 0 : 1;
        }
    
        f[0][0] = 1;
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < 1 << m; ++j) {
                f[i][j] = 0;
                for (int k = 0; k < 1 << m; ++k) {
                    // 上面是1下面必须是0
                    // 0必须是连续偶数个
                    if ((j & k) == 0 && in_s[j | k]) {
                        // 满足要求后加上i-1行k的所有情况
                        f[i][j] += f[i - 1][k];
                    }
                }
            }
        }
    
        // 最后一行的j所有位都是0
        return f[n][0];
    }
    
    int main() {
    #ifdef __MSYS__
        freopen("test.txt", "r", stdin);
    #endif
    
        int n, m;
    
        while (cin >> n >> m && n) {
            cout << solve(n, m) << endl;
        }
        return 0;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    leetcode108 Convert Sorted Array to Binary Search Tree
    leetcode98 Validate Binary Search Tree
    leetcode103 Binary Tree Zigzag Level Order Traversal
    leetcode116 Populating Next Right Pointers in Each Node
    Python全栈之路Day15
    Python全栈之路Day11
    集群监控
    Python全栈之路Day10
    自动部署反向代理、web、nfs
    5.Scss的插值
  • 原文地址:https://www.cnblogs.com/RioTian/p/13738116.html
Copyright © 2011-2022 走看看