题目
https://leetcode.cn/problems/count-number-of-ways-to-place-houses/
题解
由于道路两边的房子彼此互不影响,因此满足相互独立的条件,故而两侧的方案的乘积就是最后的答案。
因为两侧空地的数量都是 \(n\),因此只要算出其中一侧的方案即可,另一侧的方案相同。
每块空地上都可以选择建或者不建房子,但建房子后,其左右两侧相邻空地都不能再建房子。基于此,定义一个二维数组 \(dp[N][2]\),其含义为:
- \(dp[i][0]\):不在第 \(i\) 块空地上建房子的方案数
- \(dp[i][1]\):在第 \(i\) 块空地上建房子的方案数
对于第 \(0\) 块空地(也就是没有任何空地的情况),明显无法建任何房子,但没建本身也是一种方案。因此:$$dp[0][0] = 1, dp[0][1] = 0$$
对于第 \(1\) 块空地,不建房子的情况,第 \(0\) 块(即紧邻的上一块空地)可以建也可以不建。建房子的情况,第 \(0\) 块则只能不建。因此:$$dp[1][0] = dp[0][0] + dp[0][1] = 1,dp[1][1] = dp[0][0] = 1$$
对于第 \(i\) 块空地,有:$$dp[i][0] = dp[i - 1][0] + dp[i - 1][1],dp[i][1] = dp[i - 1][0]$$
那么,对于每侧有 \(n\) 块空地的建房子总方案数为:$$(dp[n][0] + dp[n][1]) * (dp[n][0] + dp[n][1])$$
对上述过程,我们可以观察到:
- \(dp[1][0] = dp[0][0] + dp[0][1] = 1,dp[1][1] = dp[0][0] = 1\)
- \(dp[1][1] = dp[0][0] = 1\)
- \(dp[2][0] = dp[1][0] + dp[1][1] = 2\)
- \(dp[2][1] = dp[1][0] = dp[0][0] + dp[0][1] = 1\)
- ...
- \(dp[i][0] = dp[i - 1][0] + dp[i - 1][1]\)
- \(dp[i][1] = dp[i - 1][0] = dp[i - 2][0] + dp[i - 2][1]\)
那么可以优化为滚动数组,将二维数组优化为一维数组:$$dp[i] = dp[i - 1] + dp[i - 2]$$
优化为一维数组后的总方案数为:\(dp[n] * dp[n]\)
参考代码
二维dp数组
constexpr int MOD = 1e9 + 7;
constexpr int N = 1e4 + 1;
int dp[N][2] = {{1, 0}, {1, 1}};
int init = []() {
for (int i = 2; i < N; ++ i) {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % MOD;
dp[i][1] = dp[i - 1][0];
}
return 0;
}();
class Solution {
public:
int countHousePlacements(int n) {
return (1LL * dp[n][0] + dp[n][1]) % MOD * (1LL * dp[n][0] + dp[n][1]) % MOD;
}
};
一维dp数组
constexpr int N = 1e4 + 1;
constexpr int MOD = 1e9 + 7;
int dp[N] = {1, 2};
int init = []() {
for (int i = 2; i < N; ++ i) {
dp[i] = (dp[i - 1] + dp[i - 2]) % MOD;
}
return 0;
}();
class Solution {
public:
int countHousePlacements(int n) {
return 1LL * dp[n] * dp[n] % MOD;
}
};