算法题题解 | 动态规划-蒙德里安的梦想

这道题目对我来说难度比较大, 第一遍没看懂, 最近有空又看了一下, 借鉴了其他博主的方法, 终于看懂啦, 记录一下, 也分享给大家~

题目: 291. 蒙德里安的梦想 - AcWing题库https://www.acwing.com/problem/content/293/

分析方法:Acwing291. 蒙德里安的梦想:状态压缩dp_阿正的梦工坊的博客-CSDN博客https://lishizheng.blog.csdn.net/article/details/112706309代码:

(代码来源于acwing y总的题解, 根据他的讲解我自己添加了注释, 所以可能有一些错误. 也参考了上面链接里博主的代码注释)

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 12, M = 1 << N;

int n, m;//矩形的行数和列数
long long f[N][M];//第一维表示列 第二维表示状态, 二进制表示(比如00100) 大数 用longlong
bool st[M];//存储每种状态是否有奇数个连续的0,如果奇数个0是无效状态false,如果是偶数个零置为true。

int main()
{

    while(cin >> n >> m, n || m)//读入棋盘的宽和长 不是0就继续
    {
        //把所有f置成0
        memset(f, 0, sizeof f);//用memset函数给f填充为0长度是f的长度 加头文件<cstring>

        //预处理1,每列不能有奇数个连续的0
        //所有状态是:从0到1 左移n位 表示2^n^ ??
        for(int i = 0; i < 1 << n; i++)
        {
            st[i] = true;//某种状态没有奇数个连续的0则标记为true
            int cnt = 0;//cnt记录当前这一段连续0的个数
            //遍历这一列 从上到下
            for(int j = 0; j < n; j++ )
                if((i >> j) & 1)//i >> j 位运算,表示i(状态)的二进制数的j位; &1为判断该位是否为1, 如果为1则进入if
                {
                    if(cnt & 1) st[i] = false;//前面连续的0的个数是不是奇数个(cnt &1为真) 说明该状态不合法的
                    cnt = 0;//既然该位是1 并且经过上面if判断,该位前面不是奇数个0, 计数器可以清零.
                } else cnt ++;

            if(cnt & 1) st[i] = false; //判断最后一段0的个数 奇数个则false
        }

        //下面是dp的过程
        f[0][0] = 1;//第一个0 最开始第0列什么都还没有放, 所以不会有横着的小方块伸过来 所以第二个0, 只有这一种方案

        for(int i = 1; i <= m; i ++)//枚举所有的列 i 的范围是1-m
            for(int j = 0; j < 1 << n; j ++)//枚举i列的所有状态j 一共有2^n^种状态 比如0000 1111..
                for(int k = 0; k < 1 << n; k++)//枚举第i-1列的所有状态k 如果真正可行 就转移
                    if((j & k) == 0 && st[j | k])//i-1列的状态k 和 i列的状态j. 按位与, 结果等于0表示没有有冲突的行. 且状态判断为true
                        f[i][j] += f[i - 1][k];// 当前i列的方案数就等于第i-1列所有状态k的累加。
        cout << f[m][0] << endl;
        //答案就是 f[m] [0].
        //即前m-1列全部摆好, 且从第m-1列到m列状态是0(即从第m-1列到第m没有伸出来的) 所有方案,即整个棋盘全部摆好的方案.
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值