E - 积木画(状态压缩DP)

news/2024/11/20 21:17:26/

E - 积木画(状态压缩DP)

1、问题

E - 积木画

2、分析

这道题很明显是一道DP题,而且是一个简化版的状态压缩DP。

(1)状态表示

f[i][j]f[i][j]f[i][j]表示前面i−1i-1i1已经摆好,并且第iii列的状态是jjj的条件下,所有的摆法数量。·
为什么这里是i−1i-1i1呢,请看下面的讲解。

(2)状态转移

在状态转移之前,我们先看如下规定:
在这里插入图片描述

很明显,f[i][j]f[i][j]f[i][j]f[i+1][j]f[i+1][j]f[i+1][j]是两个截然不同的状态,那么当我们在第iii列放积木的时候,由于积木形状的特殊性,我们不难发现,第iii列放的积木会影响到第i+1i+1i+1列的状态,而正是这种影响在两个截然不同的状态之间搭建起了一个桥梁,即实现了状态的转移,因此我们接下来的讨论就是根据第iii列的不同的积木的摆法,去写转移方程。

在讲解之前,我们需要给各个状态做一下标记:
详细地状态规定如下图所示:
在这里插入图片描述
现在开始直接讨论iiii+1i + 1i+1列的两种状态。

我们去枚举第iii列的所有状态,黑色表示iii列自带的状态,红色和橙色表示我们在第iii列所有能填的情况。所有的情况如下图所示:
在这里插入图片描述

将上图转化为状态转移方程:
f[i+1][0]=f[i][0]+f[i][3]f[i+1][1]=f[i][0]+f[i][2]f[i+1][2]=f[i][0]+f[i][1]f[i+1][3]=f[i][0]+f[i][1]+f[i][2]+f[i][3]f[i+1][0]=f[i][0]+f[i][3] \\f[i+1][1]=f[i][0]+f[i][2] \\f[i+1][2]=f[i][0]+f[i][1] \\f[i+1][3]=f[i][0]+f[i][1]+f[i][2]+f[i][3] f[i+1][0]=f[i][0]+f[i][3]f[i+1][1]=f[i][0]+f[i][2]f[i+1][2]=f[i][0]+f[i][1]f[i+1][3]=f[i][0]+f[i][1]+f[i][2]+f[i][3]

(3)初末状态

初始状态的话,我们只需要让f[1][0]=1f[1][0] = 1f[1][0]=1即可。即我们在第111列没有积木填充的方案数是111。为什么呢?
因为当前列的状态是由前一列的积木的放置状况决定的,而第000列不存在,所以没法放积木,所以第111列的状态只有000是合法的。
末状态即(f[n][3]+f[n][0])(f[n][3] +f[n][0])(f[n][3]+f[n][0])或者写作f[n+1][0]f[n+1][0]f[n+1][0]

这里解释一下为什么前面要写成f[n][3]+f[n][0]f[n][3]+f[n][0]f[n][3]+f[n][0]
因为如果第iii列是空的,所以我们可以自动填充一个竖着的积木即可。

(4)滚动数组优化

由于这道题的数据范围很大,我们的DP数组可能存不下,而我们发现我们每次存储都只用到了上一层的数据,对于上一层之前的数据其实是没有用的,也就是说我们只需要在两层之间不断地迭代滚动即可,无需开这么大的空间。
那么滚动的方法即我们将下标和1取&\&&运算即可。

3、代码

#include<bits/stdc++.h>
#define endl '\n'
#define INF 0x3f3f3f3f
#define int long long 
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
int f[3][4];void solve()
{int n;cin >> n;f[1][0] = 1;for(int i = 1; i <= n; i ++ ){f[i + 1 & 1][0] = (f[i & 1][0] + f[i & 1][3]) % mod;f[i + 1 & 1][1] = (f[i & 1][0] + f[i & 1][2]) % mod;f[i + 1 & 1][2] = (f[i & 1][0] + f[i & 1][1]) % mod;f[i + 1 & 1][3] = (f[i & 1][0] + f[i & 1][1] + f[i & 1][2]) % mod;}cout << f[n + 1 & 1][0] << endl;
}
signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);solve();
}

http://www.ppmy.cn/news/34774.html

相关文章

【数据结构与算法】什么是双向循环链表?以及实现过程

文章目录前言&#xff1a;一、相关概念二、实现过程三、总结前言&#xff1a; 线性表是我们最常用的一种数据结构&#xff0c;线性表包含顺序表和链表&#xff0c;顺序表典型应用就是我们常用的ArrayList&#xff0c;链表的典型应用其中就有我们常用的LinkedList。LinkedList他…

【C语言初阶】循环语句

文章目录&#x1f490;专栏导读&#x1f490;文章导读&#x1f337;什么是循环&#x1f337;while循环&#x1f337;do while循环&#x1f337;for循环&#x1f337;循环结构中的break与continue&#x1f33a;break&#x1f33a;continue&#x1f337;goto语句&#x1f490;专栏…

打怪升级之FPGA组成原理(LE部分)

FPGA芯片逻辑单元的原理 不论你使用哪一款FPGA芯片&#xff0c;其核心可编程逻辑单元都是从一段内存种按顺序读取执行并执行的过程。具体来说&#xff0c;FOGA芯片内部包括可编程逻辑块(LAB)、可配置输入输出单元(IOE)、时钟管理模块、嵌入式RAM(BRAN&#xff0c;在Cyclone IV…

【Vue】快速入门

目录 一、前言 二、Vue简介 2.1 vue是什么 2.2 谁开发的 2.3 Vue的特点 三、搭建Vue开发环境 3.1 安装Vue Devtools 3.2 关闭开发模式提示信息 3.3 遇到的问题 四、HelloWorld小实例 4.1 案例制作 4.2 案例分析 五、结语 一、前言 大家好&#xff0c;我是初心&…

【总结】面向对象

面向对象 1. 编程思维 根据面对问题不同人层显示的思维模式不同&#xff0c;将编程思维分为三种&#xff1a; 1&#xff09;面向过程编程&#xff08;穷人思想&#xff09; - 遇得到问题想到的是解决这个问题的具体逻辑和步骤 2&#xff09;函数式编程&#xff08;小资思想…

[前端笔记037]vue2之vuex

前言 本笔记参考视频&#xff0c;尚硅谷:BV1Zy4y1K7SH p105 - p116 vuex简介和基本使用 概念&#xff1a;专门在 Vue 中实现集中式状态&#xff08;数据&#xff09;管理的一个 Vue 插件&#xff0c;对 vue 应用中多个组件的共享状态进行集中式的管理&#xff08;读/写&…

hive的环境搭建

hive的tar包下载地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1m3VKT2-kIgR1QyjmfnWvGw?pwdr45r 提取码&#xff1a;r45rmysql的tar包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1--s1m3hfNNKEVGkFEqi5iA?pwdb7h4 提取码&#xff1a;b7h4由于hive的元…

C语言——字符函数和字符串函数【详解】(一)

文章目录函数介绍1.strlen2.strcpy3. strcat4. strcmp5. strncpy6. strncat7. strncmp8. strstr函数介绍 求字符串长度 strlen 长度不受限制的字符串函数&#xff08;使用时不安全&#xff09; strcpy strcat strcmp 长度受限制的字符串函数介绍&#xff08;与长度不受限制函数…