POJ - 2411 Mondriaan's Dream (状态压缩)

题目大意:要在n * m的网格上面铺满1 * 2或者 2 * 1的砖块,问有多少种铺放的方式

解题思路:刚开始用了3进制表示每行的状态,0表示的是2 * 1的砖块的一部分,1表示的是1 * 2的砖块的上部分,2表示的是1 * 2的砖块的下部分,然后像poj-1185炮兵阵地 那题一样去解决就好了,结果发现状态太多了,会TLE,只得放弃了

后面参考了下别人的代码,可以将其转换成二进制表示形式的,0代表没该位置没被铺到,1代表该位置有被铺到

因为有1 * 2的这种影响两行的砖头存在,所以要判断两个状态能否吻合,只有吻合了才可以

分类讨论

1.如果第i行的第j列是1

A.如果第i-1行的第j列也是1,那么这里铺的砖就不可能是1*2这种类型的砖(影响2行的砖),也就是他们两个铺的都是2*1的砖,那就要判断其相邻的那个是不是也是1了,如果不是,就表示两种状态不吻合了

B.如果第i-1行的第j列是0,那么表示第i行第j列的砖是1 * 2的砖,上部分将第i-1行的第j列给铺了

2.如果第i行的第j列是0

A.如果第i-1行的第j列也是0,那么就出现空缺了,不符合

B.如果第i-1行的第j列是1,就表示第i行需要第i+1行铺1 *2的砖来填补了

可参考大神的题解这里写链接内容

A的代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 15
#define maxs (1 << 12)
long long ans[maxn][maxn];
long long dp[maxn][maxs];
int h, m;

bool ok(int s) {
    for(int i = m - 1; i >= 0; i--) {
        if(s & (1 << i)) {
            if(i == 0)
                return false;
            if(!(s & (1 << (i - 1))))
                return false;
            i--;
        }
    }
    return true;
}

bool judge(int s, int ss) {
    for(int i = (m - 1); i >= 0; i--) {
        if(!(ss & (1 << i)) && !(s & (1 << i)))
            return false;
        if((s & (1 << i))) {
            if(ss & (1 << i)) {
                if(i == 0)
                    return false;
                if(!(s & (1 << (i - 1))) || !(ss & (1 << (i - 1))))
                    return false;
                i--;
            }
        }
    }
    return true;
}

void solve() {
    int t;
    if(h < m) {
        t = h;
        h = m;
        m = t;
    }

    memset(dp, 0, sizeof(dp));
    for(int i = 0; i < (1 << m); i++)
        if(ok(i))
            dp[1][i] = 1;   

    for(int r = 2; r <= h; r++)
        for(int s = 0; s < (1 << m); s++)
            for(int ss = 0; ss < (1 << m); ss++)
                if(judge(s, ss))
                    dp[r][s] += dp[r-1][ss];

    printf("%lld\n", ans[h][m] = ans[m][h] = dp[h][(1 << m) - 1]);
}

int main() {
    memset(ans, -1, sizeof(ans));
    while(scanf("%d%d", &h, &m) != EOF && h + m) {
        if(ans[h][m] != -1) {
            printf("%lld\n", ans[h][m]);
            continue;
        }
        if((h * m) % 2 == 1) {
            ans[h][m] = ans[m][h] = 0;
            printf("0\n");
            continue;
        }
        solve();
    }
    return 0;
}

三进制超时的代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 270000
int state[maxn];
int dp[12][maxn];
int w, h, cnt;
int mod[13];

void init() {

    memset(dp, 0, sizeof(dp));
    cnt = 0;
    bool flag1, flag2;
    for(int i = 0; i < mod[w]; i++) {
        flag1 = flag2 = false;
        for(int j = w - 1; j >= 0; j--) {
            if((i % mod[j + 1] - i % mod[j]) / mod[j] == 0) {
                if(j == 0) {
                    flag1 = true;
                    break;
                }else {
                    if((i % mod[j] - i % mod[j - 1]) / mod[j - 1] != 0) {
                        flag1 = true;
                        break;
                    }
                }
                j--;
            }
        }

        if(!flag1) {
            for(int j = w - 1; j >= 0; j--) {
                if((i % mod[j + 1] - i % mod[j]) / mod[j] == 2) {
                    flag2 = true;
                    break;
                }
            }
        }
        if(!flag1)
            state[cnt++] = i;
        if(!flag1 && !flag2)
            dp[0][i] = 1;
    }
}

int solve() {
    for(int r = 1; r < h; r++)
        for(int i = 0; i < cnt; i++)
            for(int j = 0; j < cnt; j++) {
                bool flag = false;
                for(int k = w - 1; k >= 0; k--) {
                    if(((state[j] % mod[k + 1] - state[j] % mod[k] ) / mod[k] == 1) && ( (state[i] % mod[k + 1] - state[i] % mod[k] ) / mod[k] != 2)) {
                        flag = true;
                        break;
                    }
                    if(((state[i] % mod[k + 1] - state[i] % mod[k] ) / mod[k] == 2) && ( (state[j] % mod[k + 1] - state[j] % mod[k] ) / mod[k] != 1)) {
                        flag = true;
                        break;
                    }
                }
                if(!flag) {
                    dp[r][state[i]] += dp[r-1][state[j]];
                }
            }
    int ans = 0;
    for(int i = 0; i < cnt; i++) {
        bool flag = false;
        for(int j = w - 1; j >= 0; j--)
            if((state[i] % mod[j + 1] - state[i] % mod[j]) / mod[j] == 1) {
                flag = true;
                break;
            }
        if(!flag) {
            ans += dp[h - 1][state[i]];
        }
    }
    return ans;
}

void begin() {
    mod[0] = 1;
    for(int i = 1; i < 13; i++)
        mod[i] = mod[i - 1] * 3;
}

int main() {
    begin();
    while(scanf("%d%d", &h, &w) != EOF && h + w) {
        if((h * w) % 2 == 1)
            printf("0\n");
        else {
            init();
            printf("%d\n", solve());
        }
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

POJ - 2411 Mondriaan's Dream (状态压缩)

时间: 2024-12-22 12:41:30

POJ - 2411 Mondriaan's Dream (状态压缩)的相关文章

[POJ 2411] Mondriaan&#39;s Dream 状态压缩DP

题意 给定一个 n * m 的矩形. 问有多少种多米诺骨牌覆盖. n, m <= 11 . 实现 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 6 #define LL long long 7 inline

POJ 2411 Mondriaan&#39;s Dream(状态压缩+深搜)

每一行的填充仅与上一行有关系,每行的目的都是至少填充满上一行. 当填充到i行的时候,i-1行某列没填充必须用竖直的方格填充,这是固定的,剩下其余的则搜索填充. 用2进制的01表示不放还是放 第i行只和i-1行有关 枚举i-1行的每个状态,推出由此状态能达到的i行状态 如果i-1行的出发状态某处未放,必然要在i行放一个竖的方块,所以我对上一行状态按位取反之后的状态就是放置了竖方块的状态. 然后用搜索扫一道在i行放横着的方块的所有可能,并且把这些状态累加上i-1的出发状态的方法数,如果该方法数为0,

poj 2411 Mondriaan&#39;s Dream(状态压缩+dp)

 题意:用1*2砖块铺满n*m的房间. 思路转自:http://www.cnblogs.com/scau20110726/archive/2013/03/14/2960448.html 因为这道题输入范围在11*11之间,所以可以先打表直接输出.......... 状态压缩DP 经典覆盖问题,输入n和m表示一个n*m的矩形,用1*2的方块进行覆盖,不能重叠,不能越出矩形边界,问完全覆盖完整个矩形有多少种不同的方案 其中n和m均为奇数的话,矩形面积就是奇数,可知是不可能完全覆盖的.接着我们来看

poj 2411 Mondriaan&#39;s Dream(转态压缩)

题目链接:poj 2411 Mondriaan's Dream 题目大意:用1?2的木块填满n?m的矩阵有多少种方法. 解题思路:插头dp裸题.uva11270 #include <cstdio> #include <cstring> typedef long long ll; const int N = 13; int n, m; ll set, dp[N+5][(1<<N)+5]; void solve (int d, int s, int pos) { if (p

POJ 2411 Mondriaan&#39;s Dream

题目链接:http://poj.org/problem?id=2411 状态压缩Dynamic Programming. For each row, at ith position, 1 means that there is a block placed at this row and next row (vertically). otherwise, its 0. For the example in question, the state of For the example in que

POJ 2411.Mondriaan&#39;s Dream 解题报告

题意: 给出n*m (1≤n.m≤11)的方格棋盘,用1*2的长方形骨牌不重叠地覆盖这个棋盘,求覆盖满的方案数. Solution:                位运算+状态压缩+dp                二进制数(####)代表填完一行后这一行的状态,填满的地方为1,未填的地方为0.                显然在填第i行时,能改变的仅为第i-1行和第i行,因此要满足在填第i行时,第1~i-2行已经全部填满.                DFS一行的状态,要是填完第i行时,

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

POJ 2411 Mondriaan&#39;s Dream(状压DP)

http://poj.org/problem?id=2411 求一个n*m矩阵用1*2方块去填满的情况有几种 思路:状压dp,先预处理那些状态之间能互相到达,情况就几种,上一个两个1,下一个状态也两个1,上一个为0,下一个必须为1,还有一种是上一个为1,下一个为0的情况 然后就一层层往后递推即可 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; int

POJ 2411 Mondriaan&#39;s Dream (状压DP)

题意:给出一个n*m的棋盘,及一个小的矩形1*2,问用这个小的矩形将这个大的棋盘覆盖有多少种方法. 析:对第(i,j)位置,要么不放,要么竖着放,要么横着放,如果竖着放,我们记第 (i,j)位置为0,(i+1,j)为1,如果横着放,那么我们记 (i,j),(i,j+1)都为1,然后dp[i][s]表示 第 i 行状态为 s 时,有多少方法,那么我们就可以考虑与和匹配的状态,这个也很容易判断. 代码如下: #pragma comment(linker, "/STACK:1024000000,102