ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NC51189 Mondriaan's Dream

2022-09-03 01:00:08  阅读:159  来源: 互联网

标签:状态 rectangles Mondriaan 一行 dp NC51189 Dream rectangle he


题目链接

题目

题目描述

Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series' (where he had to use his toilet paper to draw on, for all of his paper was filled with squares and rectangles), he dreamt of filling a large rectangle with small rectangles of width 2 and height 1 in varying ways.
img
Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!img

输入描述

The input contains several test cases. Each test case is made up of two integer numbers: the height h and the width w of the large rectangle. Input is terminated by h=w=0. Otherwise, 1≤h,w≤111\leq h,w\leq 111≤h,w≤11.

输出描述

For each test case, output the number of different ways the given rectangle can be filled with small rectangles of size 2 times 1. Assume the given large rectangle is oriented, i.e. count symmetrical tilings multiple times.

示例1

输入

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

输出

1
0
1
2
3
5
144
51205

题解

知识点:状压dp。

一道经典的状压dp,状态设置十分巧妙。考虑一行一行摆,因为要摆满且有两种块,单纯用 \(01\) 表示横着摆竖着摆会出问题,竖着的会影响下一行,横着的要连着放,非常麻烦。考虑设 \(dp[i][st]\) 表示考虑第 \(i\) 行, 且状态是 \(st\) ,其中 \(1\) 代表这格会延长至下一行,\(0\) 代表这格不会延长至下一行。这样,如果上一行状态是 \(st1\) ,这一行状态是 \(st2\) ,\(st1 | st2\) 中 \(1\) 表示这一行哪些格子被竖着的块占据了,两行的 \(1\) 都对应一个竖块,剩下的 \(0\) 则是横的,于是统计连续出现的 \(0\) 是否为奇数,如果是则状态组合 \(st1 | st2\) 不可能发生。

先预处理出状态组合 \(st1|st2\) 是否可能发生,共 \(2^m\) 种可能,记录在 \(vis\) 数组。

接下来枚举行,本层状态和上层状态,满足 j & k || !vis[j | k] == 1 则不合法,否则有转移方程:

\[dp[i][j] = \sum dp[i-1][k] \]

时间复杂度 \(O(n \cdot 4^m)\)

空间复杂度 \(O(n \cdot 2^m)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

ll dp[12][1 << 11];///第 i 行,状态 j,0表示横着放中的一格或者上上层竖着放下来的(即没有往下延伸),1表示竖着放(往下延伸了)
bool vis[1 << 11];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    while (cin >> n >> m, n || m) {
        for (int i = 0;i < (1 << m);i++) {///这里的状态不一样,1 表示单独存在的,0 表示是连续两个的
            vis[i] = 1;
            bool ok = 1;
            for (int j = 1;j <= m;j++) {
                if (((i >> (j - 1)) & 1)) vis[i] &= ok, ok = 1;
                else ok ^= 1;
            }
            vis[i] &= ok;///最后没有1的残留状态转移
        }
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for (int i = 1;i <= n;i++) {
            for (int j = 0;j < (1 << m);j++) {///本层
                for (int k = 0;k < (1 << m);k++) {///上层
                    if (j & k || !vis[j | k]) continue;
                    ///第一个判断1是否重叠,因为上面1,下面不能放1
                    ///第二个判断,把上面戳下来的一起当作1表示独立一格,记录连在一起的0的个数是否是偶数
                    dp[i][j] += dp[i - 1][k];
                }
            }
        }
        cout << dp[n][0] << '\n';///最终一定是全0,不会往下戳的情况
    }
    return 0;
}

标签:状态,rectangles,Mondriaan,一行,dp,NC51189,Dream,rectangle,he
来源: https://www.cnblogs.com/BlankYang/p/16651797.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有