• hiho_1048_状态压缩2


    题目大意

        用1x2的单元拼接出 NxM的矩形,单元可以横放或者纵放,N < 1000, M <= 5. 求不同的拼接方案总数。

    分析

        计算机解决问题的基本思路:搜索状态空间。如果采用dfs进行搜索,则可以将当前搜索的位置(i, j)作为状态,而不保存棋盘的占用情况,每次扩展时进行判断是否可行。这样可以得到最后的结果,但当然会超时。 
        (1) 参考hiho_1048中的提示,可以知道在搜索的时候按照固定的顺序可以大幅度剪枝(通过将无序的搜索变成有序的搜索来实现)。然后可以对中间结果进行保存,即记忆化搜索。状态dp[i][j][s1][s2] 表示求第i行,第j列位置开始扩展,且第i行的占用状态为s1, 第i+1行的占用状态为s2 时,填满剩余的空格的方案总数。

        (2)将记忆化搜索直接使用动态规划来实现,状态 dp2[i][k1][k2] 表示前i-1行都已经填满了,此时第i行的占用情况的二进制表示转换为十进制为 k1,第i+1行的占用情况的二进制表示转换为十进制为 k2,达到此时棋盘占用情况的摆放方案总数。 
        这样,可以进行状态转移时,枚举第i行的占用情况k1,和第i+1行的占用情况k2,看是否可以进行扩展,blabla...

    实现

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    #define BIT(i, j) (i & (1 << (j - 1)))
    const int mod = 1000000007;
    int dp[1005][5][35][35];
    
    /*记忆化搜索
    n,m为边界n行,m列。
    求第i行,第j列位置开始扩展,且第i行的占用状态为s1, 第i+1行的占用状态为s2 时,填满剩余的空格的方案总数
    */
    int dfs(int n, int m, int i, int j, int s1, int s2){
        if (dp[i][j][s1][s2] != -1)
            return dp[i][j][s1][s2];
        int result = 0;
        //边界!! 无法继续扩展时,取值为1,表示所有扩展到此的合理情况的初始均为1
        if (i > n || j > m || s1 >= (1 << m) || s2 >= (1 << m))
            return 1;
        //如果第i行位置j(从右向左)被占用,且j < m,向左扩展
        if (BIT(s1, j) && j < m)
            result = dfs(n, m, i, j + 1, s1, s2);
        //如果第i行位置j(从右向左)被占用,且j = m,向下一行扩展
        else if (BIT(s1, j) && j == m)
            result = dfs(n, m, i + 1, 1, s2, 0);
        //如果第i行位置j没有被占用,且无法向左或者向下扩展
        else if (BIT(s1, j) == 0 && (j == m || BIT(s1, j + 1)) && (i == n || BIT(s2, j)))
            result = 0;
        //如果第i行位置j没有被占用,且只能向左扩展
        else if (BIT(s1, j) == 0 && j < m && BIT(s1, j + 1) == 0 && (i == n || BIT(s2, j)))
            result = dfs(n, m, i, j, s1 | (1 << (j - 1)) | (1 << j), s2);
        //如果第i行位置j没有被占用,且只能向下扩展
        else if (BIT(s1, j) == 0 && (j == m || BIT(s1, j + 1)) && (i < n && BIT(s2, j) == 0))
            result = dfs(n, m, i, j, s1 | (1 << (j - 1)), s2 | (1 << (j - 1)));
        //如果第i行位置j没有被占用,且可以向左或向下扩展
        else if (BIT(s1, j) == 0 && j < m && BIT(s1, j + 1) == 0 && i < n && (BIT(s2, j) == 0))
            result = dfs(n, m, i, j, s1 | (1 << (j - 1)) | (1 << j), s2) + 
            dfs(n, m, i, j, s1 | (1 << (j - 1)), s2 | (1 << (j - 1)));
        //记忆化保存
        return dp[i][j][s1][s2] = result % mod;
    }
    
    //动归数组, dp2[i][k1][k2] 表示前i-1行都已经填满了,此时第i行的占用情况的二进制表示转换为十进制为 k1,
    // 第i+1行的占用情况的二进制表示转换为十进制为 k2
    int dp2[1005][35][35];
    int main(){
        int n, m;
        scanf("%d %d", &n, &m);
        /*
        记忆化搜索
        memset(dp, -1, sizeof(dp));
        int result = dfs(n, m, 1, 1, 0, 0);
        printf("%d
    ", result);
        */
    
        //动态规划
        memset(dp2, 0, sizeof(dp2));
        //初始状态,第1行未被占用,只有1种可能。
        dp2[1][0][0] = 1;
        int mx = 1 << m;
        for (int i = 1; i <= n; i++){
        /*
          下面的 j (第i行从右到左的每个位置)循环放在 k1(第i行当前的bit状态) 之前是为了去除重复! 因为我们总是按照顺序从上到下,从右到左枚举当前可以放置方块的位置点,
          在循环到每个(i, j)点,都看当前的哪些k1状态可以在次扩展
        */
    for (int j = 1; j <= m; j++){ //枚举第i行的所有情况(占用情况的二进制表示转换为十进制) for (int k1 = 0; k1 < mx; k1++){ //如果第i行的第j个位置(从右向左)为0,则可以从这里开始扩展 if ((k1 & (1 << (j - 1))) == 0){ //看是否可以横向扩展 if (j < m && ( ((1 << j) & k1) == 0)){ //状态转移 for (int k2 = 0; k2 < mx; k2++){ dp2[i][k1 | (1 << j) | (1 << (j - 1))][k2] = (dp2[i][k1 | (1 << j) | (1 << (j - 1))][k2] + dp2[i][k1][k2]) % mod; } } if (i < n) //看是否可以纵向扩展 //当前的第i+1行的占用情况 for (int k2 = 0; k2 < mx; k2++){ //如果第i+1行的第j个位置(从右向左)为0,则可以纵向扩展 if (((1 << (j-1)) & k2) == 0) //状态转移 dp2[i][k1 | (1 << (j - 1))][k2 | (1 << (j - 1))] = (dp2[i][k1 | (1 << (j - 1))][k2 | (1 << (j - 1))] + dp2[i][k1][k2]) % mod; } } } } //当第i行全部被占用满时,可以将第i+1行的状态 转移到 i+1行的当前行 for (int k = 0; k < mx; k++){ dp2[i + 1][k][0] = dp2[i][mx - 1][k]; } } //最后的结果是n行全部占满,此时第n行为 mx-1, 第n+1行为0 printf("%d ", dp2[n][mx-1][0]); return 0; }
  • 相关阅读:
    android如何在代码中设置margin
    也许游戏 它P/N图分析
    【淡墨Unity3D Shader计划】四 热带雨林的文章: 排除、深度测试、Alpha测试和基本雾编译
    HDU 3060 多边形面积并
    onmouseover和onmouseout的烦恼
    LoaderManager使用具体解释(三)---实现Loaders
    [每天一个Linux小技巧] gdb 下一次运行多个命令
    VB.NET版机房收费系统—DataGridView应用
    数据结构导论第一遍
    轻松搞定面试中的二叉树题目
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/5507206.html
Copyright © 2020-2023  润新知