E - 积木画(状态压缩DP)
1、问题
E - 积木画
2、分析
这道题很明显是一道DP题,而且是一个简化版的状态压缩DP。
(1)状态表示
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前面
i
−
1
i-1
i−1已经摆好,并且第
i
i
i列的状态是
j
j
j的条件下,所有的摆法数量。·
为什么这里是
i
−
1
i-1
i−1呢,请看下面的讲解。
(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();
}