DeepSeek LeetCode 1473. 粉刷房子 III public int minCost(int[] houses, int[][] cost, int m, int n, int
定义= 粉刷完前 i 个房子,第 i 个房子颜色为j,形成k 个街区的最小花费i:0…mj:1…n(颜色)k:1…target状态定义:三维 DP,核心是(位置, 颜色, 街区数)已上色的房子:只能选择固定颜色,花费为 0街区数变化规则:颜色相同则不变,不同则 +1时间复杂度:O(m × n² × target) 可优化到 O(m × n × target)边界条件:第 0 个房子用虚拟颜色 0,
·
这道题是一个三维动态规划问题,比标准的"粉刷房子"复杂很多,因为引入了**街区(neighborhood)**的概念。
题目理解
houses[i]:第 i 个房子的颜色(0 表示未粉刷,1…n 表示已粉刷)cost[i][j]:将第 i 个房子粉刷成颜色 j+1 的花费m:房子数量n:颜色数量(1-based)target:目标街区数量
街区定义:连续相同颜色的房子属于同一个街区。
例如:[1,1,2,2,2,1] 有 3 个街区:[1,1]、[2,2,2]、[1]
DP 状态定义
定义 dp[i][j][k] = 粉刷完前 i 个房子,第 i 个房子颜色为 j,形成 k 个街区的最小花费
i:0…mj:1…n(颜色)k:1…target
状态转移方程
对于第 i 个房子(1-based),尝试所有可能的颜色 j:
if (houses[i-1] != 0 && houses[i-1] != j) {
// 第 i 个房子已固定颜色且不等于 j,跳过
continue;
}
int paintCost = (houses[i-1] == j) ? 0 : cost[i-1][j-1];
然后考虑第 i-1 个房子的颜色 j0:
if (j0 == j) {
// 颜色相同,街区数不变
dp[i][j][k] = min(dp[i][j][k], dp[i-1][j0][k] + paintCost);
} else {
// 颜色不同,街区数+1
if (k > 1) {
dp[i][j][k] = min(dp[i][j][k], dp[i-1][j0][k-1] + paintCost);
}
}
完整代码 AC
class Solution {
public int minCost(int[] houses, int[][] cost, int m, int n, int target) {
// dp[i][j][k]: 前i个房子,第i个颜色j,形成k个街区的最小花费
int[][][] dp = new int[m + 1][n + 1][target + 1];
int INF = (int)1e9;
// 初始化
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
Arrays.fill(dp[i][j], INF);
}
}
// 第0个房子,颜色0,0个街区,花费0
dp[0][0][0] = 0;
for (int i = 1; i <= m; i++) {
// 当前房子可选的颜色
int startColor = 1, endColor = n;
if (houses[i-1] != 0) {
// 房子已上色,只能选这个颜色
startColor = houses[i-1];
endColor = houses[i-1];
}
for (int j = startColor; j <= endColor; j++) {
int paintCost = (houses[i-1] == j) ? 0 : cost[i-1][j-1];
for (int k = 1; k <= target; k++) {
// 枚举前一个房子的颜色
for (int j0 = 0; j0 <= n; j0++) {
if (dp[i-1][j0][k-1] == INF && dp[i-1][j0][k] == INF) {
continue;
}
if (j0 == j) {
// 颜色相同,街区数不变
dp[i][j][k] = Math.min(dp[i][j][k],
dp[i-1][j0][k] + paintCost);
} else {
// 颜色不同,街区数+1
if (k > 0) {
dp[i][j][k] = Math.min(dp[i][j][k],
dp[i-1][j0][k-1] + paintCost);
}
}
}
}
}
}
// 取结果:前m个房子,颜色任意,恰好target个街区
int ans = INF;
for (int j = 1; j <= n; j++) {
ans = Math.min(ans, dp[m][j][target]);
}
return ans == INF ? -1 : ans;
}
}
时间复杂度优化
上面的代码是 O(m * n² * target),可以优化到 O(m * n * target):
核心优化:当计算 dp[i][j][k] 时,不需要遍历所有 j0,可以维护两个变量:
bestSame= 当前颜色 j 在上一轮的最优值bestDiff= 上一轮所有颜色的最优值
class Solution {
public int minCost(int[] houses, int[][] cost, int m, int n, int target) {
int INF = (int)1e9;
int[][][] dp = new int[m + 1][n + 1][target + 1];
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
Arrays.fill(dp[i][j], INF);
}
}
dp[0][0][0] = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
// 跳过不可用的颜色
if (houses[i-1] != 0 && houses[i-1] != j) {
continue;
}
int paintCost = (houses[i-1] == j) ? 0 : cost[i-1][j-1];
// 求上一轮的最小值和第二小值,用于 O(1) 转移
int min1 = INF, min2 = INF;
int colorOfMin1 = -1;
for (int j0 = 1; j0 <= n; j0++) {
if (dp[i-1][j0][target - 1] < min1) {
min2 = min1;
min1 = dp[i-1][j0][target - 1];
colorOfMin1 = j0;
} else if (dp[i-1][j0][target - 1] < min2) {
min2 = dp[i-1][j0][target - 1];
}
}
for (int k = 1; k <= target; k++) {
// 颜色相同的情况
dp[i][j][k] = Math.min(dp[i][j][k],
dp[i-1][j][k] + paintCost);
// 颜色不同的情况:使用上一轮的最优值
if (k > 1) {
if (colorOfMin1 != j) {
dp[i][j][k] = Math.min(dp[i][j][k],
min1 + paintCost);
} else {
dp[i][j][k] = Math.min(dp[i][j][k],
min2 + paintCost);
}
}
}
}
}
int ans = INF;
for (int j = 1; j <= n; j++) {
ans = Math.min(ans, dp[m][j][target]);
}
return ans == INF ? -1 : ans;
}
}
空间优化
由于 dp[i] 只依赖 dp[i-1],可以优化到二维:
class Solution {
public int minCost(int[] houses, int[][] cost, int m, int n, int target) {
int INF = (int)1e9;
int[][] dp = new int[n + 1][target + 1];
int[][] prev = new int[n + 1][target + 1];
for (int j = 0; j <= n; j++) {
Arrays.fill(dp[j], INF);
Arrays.fill(prev[j], INF);
}
prev[0][0] = 0;
for (int i = 1; i <= m; i++) {
for (int j = 0; j <= n; j++) {
Arrays.fill(dp[j], INF);
}
for (int j = 1; j <= n; j++) {
if (houses[i-1] != 0 && houses[i-1] != j) {
continue;
}
int paintCost = (houses[i-1] == j) ? 0 : cost[i-1][j-1];
for (int k = 1; k <= target; k++) {
// 颜色相同
dp[j][k] = Math.min(dp[j][k], prev[j][k] + paintCost);
// 颜色不同
if (k > 1) {
for (int j0 = 1; j0 <= n; j0++) {
if (j0 != j) {
dp[j][k] = Math.min(dp[j][k],
prev[j0][k-1] + paintCost);
}
}
}
}
}
// 交换 dp 和 prev
int[][] temp = prev;
prev = dp;
dp = temp;
}
int ans = INF;
for (int j = 1; j <= n; j++) {
ans = Math.min(ans, prev[j][target]);
}
return ans == INF ? -1 : ans;
}
}
关键点总结
- 状态定义:三维 DP,核心是
(位置, 颜色, 街区数) - 已上色的房子:只能选择固定颜色,花费为 0
- 街区数变化规则:颜色相同则不变,不同则 +1
- 时间复杂度:O(m × n² × target) 可优化到 O(m × n × target)
- 边界条件:第 0 个房子用虚拟颜色 0,0 个街区,花费 0
更多推荐




所有评论(0)