这是个典型的动态规划问题,重点在于找到他的递推方程。
可简单算出填满第0 1 2 3 4列个数为0 1 2 5 11;
运气好点,找到递推公式dp[i]=2*dp[i-1]+dp[i-3];
直接解决了。
但我们还是按照动态规划一步一步来。
思路分析:
-
状态定义:
- 设
dp[i][j]
表示在第i
步时,处于状态j
的方案数。其中,状态j
有三种可能性:j = 0
表示在第i
步时处于上突状态。j = 1
表示在第i
步时处于占满状态。j = 2
表示在第i
步时处于下突状态。
- 设
-
初始状态:
- 初始时,只有在第
0
步处于占满状态的方案数为1
,其他状态的方案数都为0
,即dp[0][1] = 1
。
- 初始时,只有在第
-
状态转移方程:
- 对于每一步
i
,可以根据前一步的状态推导出当前步的状态。 - 上突状态 (
j = 0
) 可以由下一步的下突状态 (j = 2
) 和当前步的占满状态 (j = 1
) 推导得到,即dp[i][0] = (dp[i - 2][1] + dp[i - 1][2]) % mod
。 - 占满状态 (
j = 1
) 可以由前一步的三种状态得到,即dp[i][1] = ((dp[i - 2][1] + dp[i - 1][1]) % mod + (dp[i - 1][0] + dp[i - 1][2]) % mod) % mod
。 - 下突状态 (
j = 2
) 可以由上一步的下突状态 (j = 0
) 和当前步的上突状态 (j = 1
) 推导得到,即dp[i][2] = (dp[i - 2][1] + dp[i - 1][0]) % mod
。
- 对于每一步
-
边界条件:
- 对于
i = 0
和i = 1
,已知初始状态,无需计算。 - 对于
i = 2
,根据情况可直接给出结果。
- 对于
-
结果输出:
- 输出填满一个大小为
2 × 2 × n
的画布所需的不同方式数,即dp[n][1]
。
- 输出填满一个大小为
综上所述,这段代码使用动态规划的思想,通过状态转移方程计算填满画布所需的不同方式数,并输出结果。
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7; // 定义取模的值
int n, dp[10000005][3]; // n表示输入的整数,dp用于存储状态
int main() {
cin >> n; // 读取输入的整数
dp[0][1] = 1; // 初始状态:在第0步处于状态1(即占满状态)的方案数为1
// 动态规划计算每一步的方案数
for (int i = 1; i <= n; i++) {
// 状态转移方程:
// 0为上突,2为下突,1为占满
dp[i][0] = (dp[i - 2][1] + dp[i - 1][2]) % mod; // 上突状态的方案数等于在第i-2步处于下突状态的方案数与在第i-1步处于占满状态的方案数之和
dp[i][1] = ((dp[i - 2][1] + dp[i - 1][1]) % mod + (dp[i - 1][0] + dp[i - 1][2]) % mod) % mod; // 占满状态的方案数等于在第i-2步处于占满状态的方案数与在第i-1步处于占满状态的方案数之和,以及在第i-1步处于上突状态的方案数与下突状态的方案数之和
dp[i][2] = (dp[i - 2][1] + dp[i - 1][0]) % mod; // 下突状态的方案数等于在第i-2步处于占满状态的方案数与在第i-1步处于上突状态的方案数之和
}
cout << dp[n][1]; // 输出填满一个大小为2 × 2 × n的画布所需的不同方式数
return 0;
}
占满状态有个很不好想到的类型,就是dp[i-2][1],本来我们都是只考虑加一个积木后的情景,因为我们不考虑除了上下突和占满的其他情况,有种情况是0000,这个情况上下两行相差2格,不属
00
于三种情况,而这样的情况,从i-2到i可以有两个横着放的I这种情况,而竖着放的I的情况与i-1加个竖着的I的情况重合了。