积木画
作者:互联网
积木画
小明最近迷上了积木画,有这么两种类型的积木,分别为 $I$ 型(大小为 $2$ 个单位面积)和 $L$ 型(大小为 $3$ 个单位面积):
同时,小明有一块面积大小为 $2 \times N$ 的画布,画布由 $2 \times N$ 个 $1 \times 1$ 区域构成。
小明需要用以上两种积木将画布拼满,他想知道总共有多少种不同的方式?
积木可以任意旋转,且画布的方向固定。
输入格式
输入一个整数 $N$,表示画布大小。
输出格式
输出一个整数表示答案。
由于答案可能很大,所以输出其对 $1000000007$ 取模后的值。
数据范围
$1 \leq N \leq {10}^{7}$。
输入样例:
3
输出样例:
5
样例解释
五种情况如下图所示,颜色只是为了标识不同的积木:
解题思路
这题用状压dp来做,$f \left( {i,j} \right)$表示已经操作完前$i-1$列(前$i-1$列的方格已经被积木全部填满),且第$i$列的状态为$j$的所有方案的集合。可以发现$j$一共又$2^{2}=4$种状态,我们用$00$来表示两个方格都没有积木;$01$表示下面的方格有,上面的方格没有;$10$表示下面的方格没有,上面的方格有;$11$表示两个方格都有积木。
我们同时规定,对于横跨两列的积木,以这个积木最左边的方块所在的列为它进行操作的列。
转移状态如下:
用一个$4 \times 4$的矩阵来表示,如果$a_{ij} = 1$,表示可以从当前列的$i$状态转移到下一列的$j$状态,$a_{ij} = 0$则表示无法转移,根据上图,得到的状态转移矩阵就是
\begin{bmatrix}
1 & 1 & 1 & 1 \\
0 & 0 & 1 & 1 \\
0 & 1 & 0 & 1 \\
1 & 0 & 0 & 0
\end{bmatrix}
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1e7 + 10, mod = 1e9 + 7; 5 6 int g[4][4] = { 7 {1, 1, 1, 1}, 8 {0, 0, 1, 1}, 9 {0, 1, 0, 1}, 10 {1, 0, 0, 0} 11 }; 12 int f[N][4]; 13 14 int main() { 15 int n; 16 cin >> n; 17 18 f[1][0] = 1; // 一开始还没有放任何积木,即第1列的状态为0 19 for (int i = 1; i <= n; i++) { 20 for (int j = 0; j < 4; j++) { // 枚举第i行的状态 21 for (int k = 0; k < 4; k++) { // 枚举可以转移到的第i+1行的状态 22 if (g[j][k]) { // 如果可以从第i行的j状态转移到第i+1行的k状态 23 f[i + 1][k] = (f[i + 1][k] + f[i][j]) % mod; // 转移 24 } 25 } 26 } 27 } 28 29 cout << f[n + 1][0]; // 表示前n行已经填满,且第n+1行的状态为0 30 31 return 0; 32 }
可以用滚动数组来优化空间,把$f$数组第一维的大小从$N$优化到$2$,AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1e7 + 10, mod = 1e9 + 7; 5 6 int g[4][4] = { 7 {1, 1, 1, 1}, 8 {0, 0, 1, 1}, 9 {0, 1, 0, 1}, 10 {1, 0, 0, 0} 11 }; 12 int f[2][4]; 13 14 int main() { 15 int n; 16 cin >> n; 17 18 f[1][0] = 1; 19 for (int i = 1; i <= n; i++) { 20 memset(f[i + 1 & 1], 0, sizeof(f[i + 1 & 1])); 21 for (int j = 0; j < 4; j++) { 22 for (int k = 0; k < 4; k++) { 23 if (g[j][k]) { 24 f[i + 1 & 1][k] = (f[i + 1 & 1][k] + f[i & 1][j]) % mod; 25 } 26 } 27 } 28 } 29 30 cout << f[n + 1 & 1][0]; 31 32 return 0; 33 }
当然,也可以从第$i-1$列的状态推到第$i$列的状态,AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1e7 + 10, mod = 1e9 + 7; 5 6 int f[2][4]; 7 8 int main() { 9 int n; 10 scanf("%d", &n); 11 12 f[1][0] = 1; 13 for (int i = 2; i <= n + 1; i++) { 14 memset(f[i & 1], 0, sizeof(f[i & 1])); 15 f[i & 1][0] = (f[i - 1 & 1][0] + f[i - 1 & 1][3]) % mod; 16 f[i & 1][1] = (f[i - 1 & 1][0] + f[i - 1 & 1][2]) % mod; 17 f[i & 1][2] = (f[i - 1 & 1][0] + f[i - 1 & 1][1]) % mod; 18 for (int j = 0; j <= 2; j++) { 19 f[i & 1][3] = (f[i & 1][3] + f[i - 1 & 1][j]) % mod; 20 } 21 } 22 23 printf("%d", f[n + 1 & 1][0]); 24 25 return 0; 26 }
参考资料
AcWing 4406. 积木画(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/3799/
标签:10,状态,int,积木,画布,方格 来源: https://www.cnblogs.com/onlyblues/p/16297192.html