zoukankan      html  css  js  c++  java
  • 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; }
  • 相关阅读:
    【纯水题】POJ 1852 Ants
    【树形DP】BZOJ 1131 Sta
    【不知道怎么分类】HDU
    【树形DP】CF 1293E Xenon's Attack on the Gangs
    【贪心算法】CF Emergency Evacuation
    【思维】UVA 11300 Spreading the Wealth
    【树形DP】NOI2003 逃学的小孩
    【树形DP】BZOJ 3829 Farmcraft
    【树形DP】JSOI BZOJ4472 salesman
    【迷宫问题】CodeForces 1292A A NEKO's Maze Game
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/5507206.html
Copyright © 2011-2022 走看看