题目链接
个人思路分析
搭积木的过程,需要在前一阶段搭出来的积木基础上进行不断更新修改,在这之中就能找出一个递推关系式,即状态转移方程,这就能让人联想到动态规划。
对于题中所述的吉姆画,每一列的情况无非三种:刚好被填满、上面一个格子凸出来、下面一个格子凸出来。
如图所示,我们想要填满第三列,有如下四种方法:在前面第1列放满的情况下,在放一块面积为2的积木;在前面第2列放满的情况下,放两块面积为2的积木;在前一列有一个格子突起的情况下,放一个面积为3的积木。
同样,这一列如果出现突起,无非下面三种情况。
因此,当我们创建一个二维数组来表示这三个状态(刚好被填满、上面一个格子凸出来、下面一个格子凸出来),即dp[N][3]
。此时,dp[i][0]
表示第 i 列刚好被填满的情况,dp[i][1]
表示第 i 列上面一个格子被填充的情况,dp[i][2]
表示第 i 列下面一个格子被填充的情。那么递推关系式即为:
dp[i][0] = (dp[i - 1][0] + dp[i - 2][0] + dp[i - 1][1] + dp[i - 1][2]);
dp[i][1] = (dp[i - 2][0] + dp[i - 1][2]);
dp[i][2] = (dp[i - 2][0] + dp[i - 1][1]);
参考代码
Java
import java.util.Scanner;
public class Main {
static int n;
static long mod = 1000000007;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
// dp[i][0] 表示第 i 列刚好被填满的情况
// dp[i][1] 表示第 i 列上面一个格子被填充的情况
// dp[i][2] 表示第 i 列下面一个格子被填充的情况
long[][] dp = new long[n + 1][3];
// 第一列刚好填满只有一种情况
dp[1][0] = 1;
// 刚好第二列填满可以竖着摆两块,也可以横着摆两块
dp[2][0] = 2;
// 两种摆L型积木的情况
dp[2][1] = dp[2][2] = 1;
for (int i = 3; i <= n; ++i) {
dp[i][0] = (dp[i - 1][0] + dp[i - 2][0] + dp[i - 1][1] + dp[i - 1][2]) % mod;
dp[i][1] = (dp[i - 2][0] + dp[i - 1][2]) % mod;
dp[i][2] = (dp[i - 2][0] + dp[i - 1][1]) % mod;
}
System.out.println(dp[n][0]);
}
}
C/C++
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl "\n"
const ll mod = 1000000007;
const int MAXN = 1e7 + 3;
int n;
ll dp[MAXN][3];
int main() {
cin >> n;
dp[1][0] = 1;
dp[2][0] = 2;
dp[2][1] = dp[2][2] = 1;
for (int i = 3; i <= n; ++i) {
dp[i][0] = (dp[i - 1][0] + dp[i - 2][0] + dp[i - 1][1] + dp[i - 1][2]) % mod;
dp[i][1] = (dp[i - 2][0] + dp[i - 1][2]) % mod;
dp[i][2] = (dp[i - 2][0] + dp[i - 1][1]) % mod;
}
cout << dp[n][0] << endl;
return 0;
}