Medium
题目描述
有一条街道,街道两侧分别有 n 个地块,每侧的地块从 1 到 n 编号。每个地块上可以放置一栋房屋。
返回房屋放置的方式数,使得同一侧的街道上没有两栋房屋相邻。由于答案可能很大,返回对 10^9 + 7 取模的结果。
注意,如果在街道一侧的第 i 个地块上放置了房屋,那么在街道另一侧的第 i 个地块上也可以放置房屋。
示例 1:
输入:n = 1
输出:4
解释:
可能的排列:
1. 所有地块都是空的。
2. 在街道一侧放置一栋房屋。
3. 在街道另一侧放置一栋房屋。
4. 在街道两侧各放置一栋房屋。
示例 2:
输入:n = 2
输出:9
解释:如上图所示,共有 9 种可能的排列。
提示:
- 1 <= n <= 10^4
解题思路
这道题需要分步骤思考:
单侧街道的动态规划:首先考虑街道一侧有 n 个地块的情况。定义 dp[i] 为前 i 个地块的合法放置方案数。每个地块有两种状态:放房屋或不放房屋。如果第 i 个地块放房屋,则第 i-1 个地块不能放;如果第 i 个地块不放房屋,则第 i-1 个地块可放可不放。
状态转移方程:设 f[i] 表示单侧 i 个地块的方案数。我们可以用两个变量表示状态:
- house: 第 i 个位置放房屋的方案数
- empty: 第 i 个位置不放房屋的方案数
状态转移为:
- 新的 house = 之前的 empty(只有前一个位置为空,当前位置才能放房屋)
- 新的 empty = 之前的 house + 之前的 empty(前一个位置无论什么状态,当前都能为空)
斐波那契规律:实际上单侧的方案数遵循斐波那契数列:f(1)=2, f(2)=3, f(n)=f(n-1)+f(n-2)。
两侧组合:由于两侧街道是独立的,总方案数 = 单侧方案数的平方。
推荐解法:使用动态规划计算单侧方案数,然后平方得到最终答案。
代码实现
class Solution {
public:
int countHousePlacements(int n) {
const int MOD = 1e9 + 7;
// 计算单侧街道的方案数
long long house = 1; // 当前位置放房屋的方案数
long long empty = 1; // 当前位置不放房屋的方案数
for (int i = 2; i <= n; i++) {
long long newHouse = empty;
long long newEmpty = (house + empty) % MOD;
house = newHouse;
empty = newEmpty;
}
long long oneSide = (house + empty) % MOD;
return (oneSide * oneSide) % MOD;
}
};
class Solution:
def countHousePlacements(self, n: int) -> int:
MOD = 10**9 + 7
# 计算单侧街道的方案数
house, empty = 1, 1 # 当前位置放房屋和不放房屋的方案数
for i in range(2, n + 1):
new_house = empty
new_empty = (house + empty) % MOD
house, empty = new_house, new_empty
one_side = (house + empty) % MOD
return (one_side * one_side) % MOD
public class Solution {
public int CountHousePlacements(int n) {
const int MOD = 1000000007;
// 计算单侧街道的方案数
long house = 1; // 当前位置放房屋的方案数
long empty = 1; // 当前位置不放房屋的方案数
for (int i = 2; i <= n; i++) {
long newHouse = empty;
long newEmpty = (house + empty) % MOD;
house = newHouse;
empty = newEmpty;
}
long oneSide = (house + empty) % MOD;
return (int)((oneSide * oneSide) % MOD);
}
}
/**
* @param {number} n
* @return {number}
*/
var countHousePlacements = function(n) {
const MOD = 1e9 + 7;
// 计算单侧街道的方案数
let house = 1; // 当前位置放房屋的方案数
let empty = 1; // 当前位置不放房屋的方案数
for (let i = 2; i <= n; i++) {
let newHouse = empty;
let newEmpty = (house + empty) % MOD;
house = newHouse;
empty = newEmpty;
}
let oneSide = (house + empty) % MOD;
return (oneSide * oneSide) % MOD;
};
复杂度分析
| 复杂度类型 | 大小 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
相关题目
. Climbing Stairs (Easy)
. House Robber (Medium)