首页 > 其他分享 >AcWing291.蒙德里安的梦想题解

AcWing291.蒙德里安的梦想题解

时间:2022-12-26 11:02:50浏览次数:52  
标签:状态 蒙德里安 演变 题解 register AcWing291 UL state 我们

题解:蒙德里安的梦想

注:

本题解内容简陋,多有不周,敬请谅解。如果有问题请在评论区留言。谢谢。由于作者能力有限,这篇题解不会给出太严谨的证明,只是旨在帮助大家更好地理解此题,具体的做法请读者自己思考。

题目简述:

求把 \(N×M\) 的棋盘分割成若干个 \(1×2\) 的长方形,有多少种方案。

如下图所示:

示意图

当 \(N=2,M=4\) 时,共有上图所示的五种方案

解题大概思路

首先,对于方案数的计算,可以考虑搜索枚举以计算答案。但是,考虑到搜索的复杂度是指数级别的(很抱歉,这里没能够想出复杂度是多少,如果有同学计算出来了可以在评论区留言),不能够通过 \(N \leq 11\) 的数据范围。于是我们需要考虑优化搜索算法。

如果大家看过闫老师的 \(DP\) 问题分析课,就会发现 \(DP\) 实际上就是对搜索的优化(其中一种实现方式就是记忆化搜索)。所以,对于这一道题,我们可以考虑使用动态规划算法来解决。

闫氏 \(DP\) 分析法主要分为两大步骤:状态表示状态计算。在大概思路里我们只讨论状态表示。我们考虑一列一列地放置长方形(我也不清楚为什么要这样表示状态,暂时将其理解为这种 棋盘式动态规划问题 的一种套路)。那么,某一列放置的方案只与上一列的放置方案有关(至于为什么接下来会进行详细说明)。为了方便记录状态,我们考虑使用状态压缩的方式存储状态。于是就有了本题的大概思路:状态压缩动态规划

算法设计

我们考虑使用闫氏 \(DP\) 分析法来解决此题:

图解

以上的描述可能过于模糊。我们来举一个例子看一看:

示意图

左侧表示状态 \(f[2][0]\) (请注意,我们行和列的编号都从 \(0\) 开始,二进制表示第 \(0\) 行为 \(2^0\) ,第 \(i\) 行为 \(2^i\) ),它可以从右侧(可能不只)的三个状态转移过来: \(f[1][4],f[1][7],f[1][1]\) 。

那么如何判断一个状态 \(f[i][state]\) 是否可以由状态 \(f[i-1][j]\) 转移而来呢?

结论是,当且仅当以下条件成立:

  1. \(state ~ \& ~ j = 0\)
  2. \(state ~ | ~ j\) 每一段连续的 \(0\) 都是偶数个

接下来我们来不严谨地粗略证明以下以上结论。

我们要证明的命题是,\(j\) 状态可以由 \(state\) 状态演变而来,并且有且仅有一种演变方案,当且仅当我们说到的上述条件成立。即

\[j状态可以由state状态演变而来,并且有且仅有一种演变方案\Leftrightarrow 上述条件成立 \]

我们需要证明以下命题:

\[\tag{1}j~状态可以由~state~状态演变而来,并且有且仅有一种演变方案\rightarrow 上述条件成立 \]

\[\tag{2}j~状态可以由~state~状态演变而来,并且有且仅有一种演变方案\leftarrow 上述条件成立 \]

我们先证明命题 \((1)\) :

如果说 \(j\) 状态可以由 \(state\) 状态演变而来,并且有且仅有一种演变方案,那么上述条件成立.

我们先说一说 \(state~\&~j = 0\) 等价于什么。这等价于 \(state\) 和 \(j\) 不能在同一位上同时都是一(这里就不给出严格证明了)。我们又可以知道,状态上某一位为一,意味着这一位对应的方格放置的是一个横向的小长方形(这里也不给出严格证明,读者可以自己想想为什么)。也就是说, \(state~\&~j = 0\) 保证了两个小方块重叠的情况不会发生。我们可以大概感受一下,如果说 \(j\) 状态可以由 \(state\) 状态演变而来,那么重叠的情况是不会发生的。

我们再说一说 \(state~|~j\) 代表着什么。 \(state~|~j\) 上某一位为零等价于 \(state\) 和 \(j\) 在这一位上都不为零,也就等价于这一位上没有摆放长方形。我们考虑先摆放横着的长方形再摆放竖着的长方形,那么如果连续的 \(0\) 是奇数个,我们从感觉上可以感知到这种情况是不可能用竖着的小方块铺满的(这里也不给出严格证明)。所以说,如果说命题 \((1)\) 是成立的。

我们再来证明命题 \((2)\) :

如果上述条件成立,那么存在且仅存在一种摆放横向小方格的方式,并且剩余部分可以由纵向的小方格铺满,并且纵向摆放的方式也有且仅有一种(感觉上应该是这样的),那么 \((2)\) 也是成立的。

于是对于某一个状态 \(state\) ,我们可以枚举每一个状态 \(j\) ,如果说 \(state\) 和 \(j\) 满足以上条件,那么 \(f[i][state]\)+=\(f[i-1][j]\)

完整代码

代码如下:

#include<iostream>
#include<bitset>
#include<vector>
#include<cstring>
#define f_inline inline __attribute__((always_inline))
using namespace std;
using UL=unsigned long;
using ULL=unsigned long long;//用于存储大型非负整数
bitset<2050> isValid;
vector<UL> partner[2050];//partner[i]存储i所对应的合法的状态
ULL f[15][2050];
f_inline bool check(register const UL num,register const UL N)//i是一个N位二进制数,返回值等价于num是否合法,即所有连续的0都是偶数个
{
    register UL cnt(0);
    for(register UL i(0);i<N;++i)
        if((num>>i)&1)
        {
            if(cnt&1)
                return false;
            cnt=0;
        }
        else
            ++cnt;
    return !(cnt&1);
}
int main()
{
    register UL N,M;
    while(cin>>N>>M,N||M)
    {
        register const UL bound(1<<N);//常量UL形式存储上限的值
        //初始化对于每一个状态是否合法的判定数组
        for(register UL i(0);i<bound;++i)
            check(i,N)?isValid.set(i):isValid.reset(i);
        //初始化对于每一个状态可以转移到其的状态,用于优化代码常数
        for(register UL i(0);i<bound;++i)
        {
            partner[i].clear();
            for(register UL j(0);j<bound;++j)
                if((!(j&i))&&isValid.test(j|i))
                    partner[i].emplace_back(j);
        }
        memset(f,0,sizeof(f)),//f数组初始化赋值为0
        f[0][0]=1;//第0列状态为0有且仅有一种方案,就是一块也不放
        for(register UL i(1);i<=M;++i)
            for(register UL j(0);j<bound;++j)
                if(!partner[j].empty())
                    for(register vector<UL>::iterator it(partner[j].begin());it!=partner[j].end();++it)
                        f[i][j]+=f[i-1][*it];
        cout<<f[M][0]<<endl;//f[M][0]就是最终的答案
    }
    return 0;
}

标签:状态,蒙德里安,演变,题解,register,AcWing291,UL,state,我们
From: https://www.cnblogs.com/dengyuxin/p/17005228.html

相关文章

  • AcWing83场周赛题解
    第一题、奇偶题目链接:https://www.acwing.com/activity/content/problem/content/7862/比较麻烦(本人做法)找出不同字符个数,再判断。#include<iostream>usingnamespac......
  • 洛谷P4146 序列终结者 题解 splay tree
    题目链接:https://www.luogu.com.cn/problem/P4146题目大意:支持:区间更新(+x)区间翻转区间查询(最大值)解题思路:几乎和AcWing2437.Splay这题一模一样。示例程序:#inc......
  • Codeforces 983 D Arkady and Rectangles 题解
    题目链接挺有意思的数据结构题,题面看着像个板子,其实还是有不少学问的。平面上一堆矩形的题目常见套路就是对\(x\)轴扫描线,\(y\)轴线段树维护,这题也不例外。我们先对坐标......
  • CF732D Exams 题解
    题目链接题目分析:首先可以发现,如果当前第\(i\)天可以完成所有考试,那么第\(i+1\)天一定也可以。因此,答案具有单调性。考虑二分,将原问题转换为判定性问题。判定是否......
  • AT_jag2018summer_day2_a 10^N+7 题解
    题目传送门题目大意有三个非负整数$x,y,z$,找到符合以下条件的最小非负整数\(n\);$n\{\rm\mod}\10^1+7\=\x$$n\{\rm\mod}\10^2+7\=\y$$n\{\rm\mo......
  • CF864C Bus 题解
    题目传送门题目大意一辆汽车从\(0\)到\(a\)往返\(k\div2\)次(也就是去算一次,回算一次);原来有\(b\)升油,每行驶一单位距离消耗一升油,在\(f\)有加油站(可以加满油......
  • UVA13197 Cuberoot This 题解
    题目传送门题目大意求满足\(x^3\bmodp=a\)且\(x<p\)的数\(x\),升序输出。解题思路在\(0\)到\(p-1\)的范围内,查找满足条件的\(x\);值得注意的是,输出要留意:最......
  • AT_joi2022_yo1a_d 箱と鍵 (Boxes and Keys) 题解
    题目传送门题目大意给定一个长度为\(n\)的数组\(a\)和一个长度为\(m\)的数组\(b\),求\(a\)中有多少个数在\(b\)中出现过。解题思路数据比较小,可以直接暴力:......
  • CF1735A Working Week 题解
    题目传送门题目大意一周有\(n\)天,有三天休息日,其中第\(n\)天一定休息。现需要安排剩下的两个休息日,要求:不能使得休息日相邻。这两个休息日将\(n-1\)天分成三......
  • AT_mujin_pc_2018_b セキュリティ 题解
    题目传送门题目大意房间原有\(A\)人,+表示进来一个人,-表示出去一个人;求是否有一个时间,房间内的人数为\(0\)。解题思路按题意进行模拟:首先判断\(A\)是否等于零,......