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

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

1、问题

E - 积木画

2、分析

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

(1)状态表示

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

(2)状态转移

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

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

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

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

将上图转化为状态转移方程:
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 ] = 1 f[1][0] = 1 f[1][0]=1即可。即我们在第 1 1 1列没有积木填充的方案数是 1 1 1。为什么呢?
因为当前列的状态是由前一列的积木的放置状况决定的,而第 0 0 0列不存在,所以没法放积木,所以第 1 1 1列的状态只有 0 0 0是合法的。
末状态即 ( 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]
因为如果第 i i i列是空的,所以我们可以自动填充一个竖着的积木即可。

(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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值