蒙德里安的梦想

这篇具有很好参考价值的文章主要介绍了蒙德里安的梦想。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

蒙德里安的梦想

算法标签

状态压缩dp

题目大意:求把 N×M的棋盘分割成若干个1×2 的的小长方形,有多少种方案。

思路分析: 首先,注意到,我们直接考虑如何切割整个棋盘为若干个1x2的长方形是比较困难的,因此,我们可以把整个棋盘划分为若干个1x1的小格子,那么,问题就转化为了用1x2的小长方形去将整个棋盘填充满,总共有多少种方案。此外,注意到,当我们将所有横向的小长方形都已经摆放(填充)完成后,所有纵向的小长方形的摆放方式也就唯一确定了,那么,总的方案数就等于摆完所有横向长方形的方案数。所以,我们只用考虑如何枚举横向长方形的摆放即可

模型: 
这个问题属于状态压缩dp问题的第一大类:基于连通性的dp,也可以叫做棋盘式dp。我们首先考虑如何定义(设计)状态,也就是如何进行状态表示。 我们按列来枚举横向小长方形的摆放情况。f为dp数组。若当前我们需要摆放的列是第i 列,那么第i列在摆放的时候只和第i-1列有关(因为只有i-1列的小长方形可能伸到第i列,影响第i 列的摆放)。因此,我们设f[i][j]表示:前i-1列已经摆放完毕(不能再改了),当前要对第i列进行横向小长方形的摆放,且前一列伸出一个小方格是j 的情况下的方案数。如下图所示:

j是一个n位的二进制数,上图对应的j为01,代表第i-1列第0行伸出了一个小方格(所以j的第0位为1)。那么,这个状态表示的集合就是:

所有前i-1列已经摆放完毕且第i-1列伸出小方格的状态为j的情况下的摆放方案

f[i][j]记录的是集合中的方案总数(根据题目所求来确定)

接下来考虑如何进行状态计算(转移),所谓状态计算,就是对集合进行划分,而划分的依据就是“最后一个不同点”。根据我们上面f[i][j]的定义,第i-1列横向小长方形的摆放方案是确定的(因为二进制数j代表了其摆放的情况),所以,最后一个不同点就是第i-2列的摆放情况。因此,我们将依据第i-2列的摆放情况对f[i][j]所表示的大集合进行划分,将它划分成1<<n个子集。但是,这些子集不一定都可以转移过来,因此,我们需要判断哪些子集所对应的状态才可以转移到我们当前的状态。为了方便之后的表示,我们设a为第i-1列的“摆放情况”,b为第i-2列的”摆放情况”。那么,合法的状态需要满足以下两个条件:1:a&b==0,即二者的二进制不能有某一位同时为1(意思是第i-2列和第i-1列不能在同一行同时摆放上横向的小长方形(同时伸出小格子),因为这样,第i-2列伸出的小方格就会”阻碍”到第i-1列的摆放,是不合法的)

2: 由于第i-1列横向的格子已经摆放完毕,那么其纵向格子的摆放方式也已经确定,所以,我们摆完横向的小长方形后,剩下连续的空格子的数量不能是奇数,否则就不能通过摆放纵向小长方形将整个棋盘填满了。所以,我们需要算出每个状态下连续0的个数。这个条件的判断可以通过”预处理完成”。

另外,我们可以预处理出所有合法(能够进行状态转移的状态),方便后续状态的计算。

这样,f[i][j]就等于所有能转移到它的状态所对应的方案数的总和。

我们最终的答案就是:f[m][0]。注意,下标从0开始,所以,它代表所有前m–1列(也就是整个棋盘)已经摆完,并且伸出格子的情况为0(没有伸出棋盘,是合法的)的方案总数。并且,dp数组的初始化为:f[0][0]=1(因为摆第0列时不存在前一列会伸出来,也就表示当前列没有横着摆的小长方形,所以只有都竖着摆放这一种情况)

c++代码

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=12,M=1<<N;
typedef long long LL;
int n,m,st[M];//st==1表示当前状态是合法的
LL f[N][M];//极限状态下可能会爆int
vector<int>truestate[M];//二维数组,truestate[i]中存储所有可以转移到状态i的状态,方便通过循环递推计算出所有状态的值
//vector<vector<int>>truestate(M);
int main()
{
    while(cin>>n>>m,n||m) //逗号表达式的值为逗号之后的值
    {
        for(int i=0;i<1<<n;i++)//枚举所有状态,判断其是否有连续奇数个0
        {
            st[i]=1;
            int cnt=0;//连续奇数0的个数
            for(int j=0;j<n;j++)
                if(i>>j&1)//i的第j位是1,前一段连续的0终止了
                {
                   if(cnt&1)//cnt为奇数
                    {
                        st[i]=0;
                        break;
                    }
                    cnt=0; //继续统计下一段连续0的个数,要将上一段的数量清零(实际上不清0也可以AC,因为加一个偶数不会影响奇偶性(奇数会break))
                }else
                    cnt++;
            if(cnt&1)//判断最后一段连续0的个数的奇偶性
                st[i]=0;
        }
        for(int i=0;i<1<<n;i++)//计算出所有的truestate 这里i枚举的是状态定义中对应的第i-1列的情况,j枚举的是第i-2列对应的情况
        {
            truestate[i].clear();
            for(int j=0;j<1<<n;j++)
                if(!(i&j)&&st[i|j])//只要二者有一位为1,连续0就会被隔断,所以,第状态定义中第i-1列实际的状态是i|j
                    truestate[i].push_back(j);
        }
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for(int i=1;i<=m;i++)//枚举所有列
            for(int j=0;j<1<<n;j++)//枚举所有状态
                for(auto &k:truestate[j])//遍历所有可以转移到j的状态
                    f[i][j]+=f[i-1][k];//状态计算(转移)
        cout<<f[m][0]<<endl;
    }
    return 0;
}

时间复杂度分析:dp问题的时间复杂度公式 =状态数量× 状态转移(计算)

状态表示 f[i][j] 第一维i可取11,第二维j(二进制数)可取2的11次方,根据乘法原理,可计算出状态数量
状态转移 也是2的11次方(每个状态最多可划分成这么多个子集)
所以总的时间复杂度约等于=4e7文章来源地址https://www.toymoban.com/news/detail-640733.html

到了这里,关于蒙德里安的梦想的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法】动态规划 ① ( 动态规划简介 | 自底向上的动态规划示例 | 自顶向下的动态规划示例 )

    动态规划 , 英文名称 Dynamic Programming , 简称 DP , 不是具体的某种算法 , 是一种算法思想 ; 具体的算法都有具体的步骤 , 如 : 二分法 , 其 有固定的解决步骤 , 先取一个中心点 , 判断解在左边还是右边 , 然后在一边再取一个中心点 , 再进行判定 , 该算法有具体的步骤 ; 动态规划

    2024年01月16日
    浏览(46)
  • 60题学会动态规划系列:动态规划算法第四讲

    买卖股票相关的动态规划题目 文章目录 1. 买卖股票的最佳时机含冷冻期 2. 买卖股票的最佳时期含⼿续费 3. 买卖股票的最佳时机III 4. 买卖股票的最佳时机IV 力扣链接:力扣 给定一个整数数组 prices ,其中第    prices[i]  表示第  i  天的股票价格 。​ 设计一个算法计算出最

    2024年02月13日
    浏览(36)
  • 60题学会动态规划系列:动态规划算法第五讲

    子数组系列题目 文章目录 1.最大子数组和 2.环形子数组的最大和 3.乘积最大数组 4.乘积为正数的最长子数组长度 5.等差数列划分 6.最长湍流子数组 7.单词拆分 8.环绕字符串中唯一的子字符串 力扣链接:力扣 给你一个整数数组  nums  ,请你找出一个具有最大和的连续子数组(

    2024年02月15日
    浏览(69)
  • 60题学会动态规划系列:动态规划算法第一讲

    坚持就是胜利 - -  文章目录 1.第N个泰波那切数 2.三步问题 3.使用最小花费爬楼梯 4.解码方法 力扣链接:力扣 泰波那契序列 Tn 定义如下:  T0 = 0, T1 = 1, T2 = 1, 且在 n = 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2 给你整数  n ,请返回第 n 个泰波那契数 Tn 的值。  首先我们分析一下

    2024年02月06日
    浏览(45)
  • 【动态规划】动态规划算法基本概念,原理应用和示例代码

             动态规划(Dynamic Programming,简称DP)是一种解决多阶段决策问题的数学优化方法。它将原问题分解成若干个子问题,通过解决子问题只需解决一次并将结果保存下来,从而避免了重复计算,提高了算法效率。         通俗来讲,动态规划算法是解决一类具有重叠

    2024年01月21日
    浏览(49)
  • 60题学会动态规划系列:动态规划算法第二讲

    都是路径问题~ 文章目录 1.不同路径 2.不同路径II 3.礼物的最大价值 4.下降路径最小和 5.最小路径和 力扣链接:力扣 一个机器人位于一个  m x n   网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在

    2024年02月07日
    浏览(59)
  • 队列中的动态规划算法:如何在队列中动态规划?

    作者:禅与计算机程序设计艺术

    2024年02月14日
    浏览(37)
  • DP算法:动态规划算法

    (1)确定初始状态 (2)确定转移矩阵,得到每个阶段的状态,由上一阶段推到出来 (3)确定边界条件。 蓝桥杯——印章(python实现) 使用dp记录状态,dp[i][j]表示买i张印章,凑齐j种印章的概率 i表示买的印章数,j表示凑齐的印章种数 情况一:如果ij,不可能凑齐印章,概

    2024年02月07日
    浏览(51)
  • 算法 - 动态规划 / 贪心算法

    🥂 🌸 121. 买卖股票的最佳时机 [数组] [股票] (动态规划) 🥂 🌸 122. 买卖股票的最佳时机Ⅱ [数组] [股票] (动态规划) 🥂 🌸 123. 买卖股票的最佳时机Ⅲ [数组] [股票] (动态规划) 🥂 🌸 188. 买卖股票的最佳时机Ⅳ [数组] [股票] (动态规划) 🥂 🌸 309. 买卖股票的最佳时机含冷冻

    2024年01月17日
    浏览(38)
  • 【算法】动态规划算法求(编辑距离)

    目录 编辑距离: 举例: 代码如下 调试: 核心代码: 画图演示上述代码:    是一种计算两个自符串之间差异程度的方法,它通过比较 两个字符串之间的插入,删除和 替换操作的数量 ,来确定他们之间的距离。 现有两个字符串 字符串s1:”CTGA\\\" 字符串s2:  \\\"ACGCTA\\\" 求s1和

    2024年02月12日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包