状态压缩dp整理

news2024/11/17 21:49:59

目录

  • 蒙德里安的梦想
    • 详细解释
    • Code
  • 最短Hamilton路径
    • 详细解释
    • Code


蒙德里安的梦想

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

例如当 N = 2 , M = 4 N=2,M=4 N=2M=4 时,共有 5 5 5 种方案。当 N = 2 , M = 3 N=2,M=3 N=2M=3 时,共有 3 3 3 种方案。

如下图所示:

在这里插入图片描述

输入格式
输入包含多组测试用例。

每组测试用例占一行,包含两个整数 N 和 M N 和 M NM

当输入用例 N = 0 , M = 0 N=0,M=0 N=0M=0 时,表示输入终止,且该用例无需处理。

输出格式
每个测试用例输出一个结果,每个结果占一行。

数据范围

1 ≤ N , M ≤ 11 1≤N,M≤11 1N,M11

输入样例:

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

详细解释

这题最最核心的一点是要想到:横着摆放方块的方案数 = 总摆放方块方案数

由于 1 × 2 1×2 1×2的方块可以横着或是竖着放,观察图给样例,如果把所有能横着放的方块都放入后,竖着放的方块位置也就固定了,只管往里塞,于是结论就是要找总方案数只需要找到可以横着摆放方块的方案数。

状态表示

f[i][j]:前i - 1列的摆放状态已处理好的前提下,在i - 1列横放的小方块伸到i列的格子状态为j的方案数。
其中j由于是一个状态表示,所以要把它想成一个二进制数,它的范围是由 n n n(一列有 n n n行)决定的,即为了表示 n n n个格子需要有 2 n 2^n 2n 个状态,即 j ∈ ( 0 , 1 < < n ) j∈(0,1 << n) j(0,1<<n)


格子的摆放引起状态

这个状态定义有点长,什么意思呢?以下图为例:
表示当 i = 2 ( i 从 0 开 始 ) i=2(i从0开始) i=2(i0) j = ( 11001 ) 2 j = (11001)_2 j=(11001)2时的状态, j j j中为 1 1 1的地方表示该列的这个位置被占了

在这里插入图片描述

其中尤其要注意的是:第 i i i列的 j j j这个状态本质上是由于第 i − 1 i-1 i1列状态造成影响的,也就是说, j j j中为1的地方在 i − 1 i-1 i1列的对应位置上是横放了方块之后捅到第 i i i 列去的

以此类推,如果定义 i − 1 i-1 i1列上的状态是 k k k,那么这个 k k k是经过 i − 2 i-2 i2列的方块放置影响到的,就像下图:

在这里插入图片描述

那么绿色的方块是在 i − 2 i-2 i2列放置导致的,此时的 k = ( 00100 ) 2 k=(00100)_2 k=(00100)2,注意 ≠ 11101 ≠11101 =11101


状态计算

由于动态规划是按列数从小到大( 0 到 m − 1 0到m-1 0m1)挨着枚举状态,因此第 i i i 列的方案数f[i][j],由前一列的方案数f[i - 1][k]转移而来,即f[i][j] = Σ(f[i - 1][k])


判断合法状态

很明显不是所有格子摆放状态都是合法的。

  1. 每一列的状态中连续空着的格子数不能为奇数,因为空格是给竖着放的方块留着的,方块尺寸 1 × 2 1×2 1×2,当要竖着放时遇到奇数空格注定是要么放不满要么放不下。用一个st[]数组存,如果状态 j j j 连续空着的格子数为偶数st[j] = true。而状态转移时是要判断方块从 i − 2 i - 2 i2列合在第 i − 1 i - 1 i1列的方块共有多少个,可以“或”运算,即判断st[j | k]
  2. i - 2列伸到i - 1列的小方格 和i - 1列放置的小方格不能重复,啥意思呢?看下图 请添加图片描述
    可以看到,此时蓝色的方块和红色的方块 “撞” 在了一起,显然非法的,必须得让 j j j k k k中的“1”岔开,这里判断用到一个小技巧,j & k == 0则意味着相同位置不会同时出现1。

最终,整块拼图的方案数存在了f[m][0],0表示 ( 00...0 ) 2 (00...0)_2 (00...0)2,它意味着 m m m列不放小方格,前 m − 1 m-1 m1列已经完全摆放好并且不伸出来的状态。那么意味着拼图完整。

请添加图片描述


Code

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 12, M = 1 << N;       //N表示列数、M表示状态的范围(0b00...0 ~ 0b10...0)

ll f[N][M];        //f[i][j]:第i列的状态为j时的方案数
bool st[M];          //st[j]映射状态j是否满足“没有奇数个连续的空格”这个条件
int n, m;

int main(){
    while(cin >> n >> m, n || m){
        //第一步预处理,装填st数组
        for(int i = 0;i < 1 << n;i ++){     //枚举每个状态
            int cnt = 0;
            bool valid = true;
            for(int j = 0;j < n;j ++){
                if((i >> j) & 1){
                    if(cnt & 1){     //如果有奇数个连续的空格
                        valid = false;
                        break;
                    }
                    cnt = 0;
                }
                else    cnt ++;
            }
            if(cnt & 1)     valid = false;      //统计最后一堆连续空格(如果有)
            st[i] = valid;
        }
        
        memset(f, 0, sizeof f);
        f[0][0] = 1;		//因为没有-1列,第0列至少有一种摆放方案
        for(int i = 1;i <= m;i ++)         //枚举每一列
            for(int j = 0;j < 1 << n;j ++)          //枚举j,把j视作从i - 1列捅到i列的格子状态
                for(int k = 0;k < 1 << n;k ++)      //枚举k,把k视作从i - 2列捅到i - 1列的格子状态
                    if((j & k) == 0 && st[j | k])
                        f[i][j] += f[i - 1][k];
                        
        cout << f[m][0] << endl;            
    }
    
    return 0;
}

最短Hamilton路径

给定一张 n n n 个点的带权无向图,点从 0 ∼ n − 1 0∼n−1 0n1 标号,求起点 0 0 0 到终点 n − 1 n−1 n1 的最短 Hamilton 路径。

Hamilton 路径的定义是从 0 0 0 n − 1 n−1 n1 不重不漏地经过每个点恰好一次。

输入格式
第一行输入整数 n n n

接下来 n n n 行每行 n n n 个整数,其中第 i i i 行第 j j j 个整数表示点 i i i j j j 的距离(记为 a [ i , j ] a[i,j] a[i,j])。

对于任意的 x , y , z x,y,z x,y,z,数据保证 a [ x , x ] = 0 , a [ x , y ] = a [ y , x ] 并 且 a [ x , y ] + a [ y , z ] ≥ a [ x , z ] a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z] a[x,x]=0a[x,y]=a[y,x]a[x,y]+a[y,z]a[x,z]

输出格式
输出一个整数,表示最短 Hamilton 路径的长度。

数据范围
1 ≤ n ≤ 20 1≤n≤20 1n20
0 ≤ a [ i , j ] ≤ 1 0 7 0≤a[i,j]≤10^7 0a[i,j]107

输入样例:

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

输出样例:

18

详细解释

这里有一篇写得很棒的解释👉 最短Hamilton路径(超详解)。
偷个懒吧hh。

状态表示

f[i][j]: 所有从0走到j,经过的路径是i(二进制数)的最短距离。
举个例子:i = 11001j = 4时,1表示走到,0表示没走到,那么状态i代表的路径就是0 —> 3 —> 4,那么f[0b11001][4]就代表从0走到4,路径是0 —> 3 —> 4的最短距离。
因此,答案就存在f[0b111...1][n - 1] = f[(1 << n) - 1][]n - 1里面。

状态计算

状态计算用到的算法就很类似于 f l o y d floyd floyd算法,即

  1. 暴力地枚举每种可能的走法i
  2. i的走法下,暴力地枚举每一个点j作为终点的情况
  3. j作为终点的基础上,然后再暴力地枚举每一个点k(这个点也可以想象成 f l o y d floyd floyd里的那个k),目的是把k视作一个中间点(从位置上准确地讲,k0走到j之前到达的最后一个点)
  4. 然后经过各种0 —> (一系列不包含终点 j 的点) —> k —> j路径来对f[i][j]进行松弛操作,也就是f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j])其中i - (1 << j)对应的就是 “一系列不包含终点 j j j 的点”

Code

#include <iostream>
#include <cstring>
using namespace std;

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

int w[N][N];        //距离矩阵
int f[M][N];        //f[i][j]:所有从0走到j,经过的路径是i(二进制数)的最短路径
int n;

int main(){
    cin >> n;
    for(int i = 0;i < n;i ++)
        for(int j = 0;j < n;j ++)
            cin >> w[i][j];
            
    memset(f, 0x3f, sizeof f);
    f[1][0] = 0;
    
    for(int i = 0;i < 1 << n;i ++)      //枚举所有的走法
        for(int j = 0;j < n;j ++)       //枚举j,将j认为是终点,当然了,是为了能将状态转移到n - 1这个点
            if(i >> j & 1)      //点在路径上的状态是1才说明“走到了”并进行处理
                for(int k = 0;k < n;k ++)      //枚举k,将k认为是走到j之前的最后一个点
                    if(i >> k & 1)		//点在路径上的状态是1才说明“走到了”并进行处理
                        f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);      //松弛操作
                        
    cout << f[(1 << n) - 1][n - 1] << endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

语音合成技术入门之Tacotron

语音合成TTS 学习李宏毅课程。 输入文字&#xff0c;输出语音。 端到端之前TTS 18世纪就有&#xff0c;能找到demo的是1939年VODER。 就像电子琴一样&#xff0c;用手控制发出不同声音。 到1960年&#xff0c;IBM计算机能合成出歌唱声。 波形拼接 过去最常用的商用语音合…

策略验证_指标买点分析技法_运用MACD确定最佳买点

写在前面&#xff1a; 1. 本文中提到的“股票策略校验工具”的具体使用操作请查看该博文&#xff1b; 2. 文中知识内容来自书籍《同花顺炒股软件从入门到精通》 3. 本系列文章是用来学习技法&#xff0c;文中所得内容都仅仅只是作为演示功能使用 目录 解说 策略代码 结果 解…

【node.js】第六章 初识express

目录 1. express简介 1.1 express的概念 1.2 express的作用 2. express的使用 2.1 使用express创建Web服务器 2.2 监听GET/POST请求 2.3 获取URL的请求参数 3. 托管静态资源 3.1 express.static 3.2 托管多个静态资源 3.3 挂载路径前缀 4. nodemon 1. express…

Docker镜像操作、容器操作、数据卷及挂载数据卷

目录 一、镜像操作 案例&#xff1a;从DockerHub中拉取一个nginx镜像并查看 案例&#xff1a;利用docker save将nginx镜像导出磁盘&#xff0c;然后再通过load加载回来 二、容器操作 案例&#xff1a;创建运行一个Nginx容器 案例&#xff1a;创建并进入redis容器&#xf…

随笔记录-看nacos源码

Import注解 Import注解可以导入一些配置类&#xff0c;也就是创建一些指定对象。 使用Import导入普通类 项目结构中&#xff0c;import-consumer和import-provider都是同层级的module&#xff0c;import-consumer的pom文件中有引用import-provider的依赖&#xff1b; import…

baby_web (攻防世界)

前言: 这篇文章还是是为了帮助一些 像我这样的菜鸟 找到简单的题解 题目描述 进入网址 解题工具: 一个有F12的键盘 问题解析: 题目说想想初始页面是哪个 一般都是index.php 然后如题分析即可 科普时间叒到 HTTP状态码 &#xff08;英语&#xff1a;HTTP Status Code…

从零开始操作系统-08:计时器

这一节主要主要是计时器。 所需要的文件在Github&#xff1a;https://github.com/yongkangluo/Ubuntu20.04OS/tree/main/Files/Lec7-ExternalInterrupt 计时器&#xff1a; 可编程间隔计时器&#xff1a;PIT&#xff08;Programmalbe Interval Timer&#xff09;8254 使用A…

Windows下文本生成图像AI画图尝鲜体验

工具库 transformers 的开源方 Hugging Face 发布了一个专注于 diffuser 模型的开源库&#xff0c;我们可以基于它&#xff0c;仅仅通过几行代码就开始生成自己的艺术作画。不过这个 diffuser 库是一个基础实现版本&#xff0c;训练和学习的数据也没有 OpenAI 的 DALL-E2、谷歌…

学习docker记录(三)

使用volume 创建一个数据卷 docker volume create test-first-volume创建完之后&#xff0c;可以在 /var/lib/docker/volumes/ 目录下看见新建了一个 test-first-volume文件夹 在Dockerfile中 volume的指定的目录是&#xff1a; /www/wwwroot/pictureManager/deploy原本以为…

三步教你快速入手一个新产品的测试

初入一家公司&#xff0c;当一个全新的产品摆在你的面前&#xff0c;你会如何快速入手呢&#xff1f; 虽说实践是熟悉系统的第一要素&#xff0c;但我们需要静静思考一下。 我是谁&#xff1a;QA 我在哪&#xff1a;产品组 我要做什么&#xff1a;保质量 有多少来熟悉&…

LeetCode 320 周赛

总结 本场周赛太拉跨了&#xff01;T1做完后&#xff0c;T2一直被卡住&#xff0c;还好后面暂时跳过了T2去做T3&#xff0c;T3做完后又回过头来继续调试T2。在最后10分钟调过了&#xff08;虽然后来看运行时长达到了1400ms&#xff08;差点就过不了&#xff09;&#xff09;。 …

vue3 斗兽棋游戏

近来掘金举办前端比赛&#xff0c;所以写了一个小游戏参加&#xff0c;而且熟悉一下vue3,写了一下游戏&#xff0c;思来想去就写了一个斗兽棋游戏。 欢迎去给我加油 点赞评论收藏 &#xff0c;试玩地址 游戏地址 童年斗兽棋 - 码上掘金 https://code.juejin.cn/pen/716…

四川水泥杂志四川水泥杂志社四川水泥编辑部2022年第11期目录

水泥与混凝土 外加剂含固量对混凝土性能影响的分析 金世鑫; 1-4 《四川水泥》投稿&#xff1a;cnqikantg126.com 粉煤灰在干混砂浆中的应用研究 方光旭;古再努尔依明;杨博;陈南希;张琰琦; 5-7 浅析透光混凝土在建筑空间中的应用 李嘉;李嘉鑫; 8-10 研究与探讨…

c++ 智能指针 shared_ptr

C 智能指针 shared_ptr 详解与示例_码农小明的博客-CSDN博客_shared_ptr 一、简介 shared_ptr 是c11的智能类&#xff0c;可以在任何地方都不使用的时候自动删除和相关指针&#xff0c;从而彻底消除内存泄漏和指针悬空的问题。 她遵循共享所有权&#xff0c;即不同的shared_pt…

CVPR‘15 Joint action recognition and pose estimation from video

任务&#xff1a;action recognition and pose estimation 思路&#xff1a;对动作和姿态进行统一建模&#xff0c;将动作分成姿态&#xff0c;再将姿态分成part&#xff0c;学习三种level特征&#xff0c;通过动态规划有效的推断动作标签和姿态。 方法&#xff1a;统一建模…

通俗理解计算机操作系统的作用

“操作系统”&#xff0c;简称“OS”&#xff0c;是一个包含多个部分和多个目标的大型程序。 它的第一项工作是在你第一次打开计算机时启动并运行计算机。它的另一项工作是启动和结束应用程序&#xff0c;并给每个程序一个运行时间。它是那台计算机上所有其他程序的“老大”。当…

【App自动化测试】(一)Appium和移动端自动化

目录1. 目前mobile自动化解决方案1.1 iOS和Android 测试工具1.2 自动化工具的选择1.3 选择自动化工具的考虑因素2. Appium介绍2.1 Appium介绍2.2 多架构支持2.3 推荐Appium的理由2.4. Appium框架介绍2.4.1 Appium引擎列表2.4.2 Appium设计理念前言&#xff1a; 本文为在霍格沃兹…

多智能体强化学习MARL的概念和框架

1.多智能体强化学习 系统里的agents数量大于1&#xff0c;agents彼此之间不是独立的 每个agent的动作都能影响到下一个状态每个agent都能影响到其他agent 除非agent之间是独立的&#xff0c;否则单一agent的RL方法不适合MARL 2.MARL的类型 Fully cooperative&#xff08;完…

智慧医院智慧医疗解决方案

IBM于2009年提出“智慧医疗”这一理念。作为“智慧的地球”战略的重要组成部分&#xff0c;致力于构建一个“以病人为中心”的医疗服务体系。通过在服务成本、服务质量和服务可及性三方面取得一个良好的平衡&#xff0c;从而优化医疗实践成果、创新医疗服务模式和业务市场&…

Unity插件Obi.Rope详解

前言 Obi.Rope插件的使用方法 绳子的创建 创建后的Obi Rope&#xff0c;Inspector面板如下所示&#xff0c;组件比较多&#xff0c;其中Obi Solver是绳子&#xff0c;布料&#xff0c;液体等的总处理器&#xff0c;也可以单独分出来此组件为一个独立的游戏对象。 绳子的形状…