【DP】LeetCode 256. 粉刷房子
题目链接
假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
示例 1:
输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。
最少花费: 2 + 5 + 3 = 10。
示例 2:
输入: costs = [[7,6,2]]
输出: 2
思路
分析动态规划题目的时候只需要考虑最后一个阶段,因为所有的阶段转化都是相同的,考虑最后一个阶段容易发现规律
表示状态
本题与常规题目不同的一点:本题需要记录三种情况下的花费,因为你不知道每个房子最终会被刷成什么颜色。什么意思呢?举个栗子:
- 第 n 个房子刷成了红色,那么第 n-1 个房子就要刷成蓝色或绿色,此时需要存储第 n 个房子刷成红色情况下的花费;
- 第 n 个房子刷成了蓝色,那么第 n-1 个房子就要刷成红色或绿色,此时需要存储第 n 个房子刷成蓝色情况下的花费;
- 第 n 个房子刷成了绿色,那么第 n-1 个房子就要刷成蓝色或红色,此时需要存储第 n 个房子刷成绿色情况下的花费;
既然我们要存储三份信息,索性就直接定义三个数组 redDP
、blueDP
和 greenDP
分别存储第 i
个房子刷成某种颜色情况下的花费。
找状态转移方程
还是从最终阶段出发,
- 第 n 个房子是红色,说明第 n-1 个房子是蓝色或绿色,此时到第 n 个房子的总花费为 \(min(blueDP[n-1], greenDP[n-1]) + cost[n][0]\)
- 第 n 个房子是蓝色,说明第 n-1 个房子是红色或绿色,此时到第 n 个房子的总花费为 \(min(redDP[n-1], greenDP[n-1]) + cost[n][1]\)
- 第 n 个房子是绿色,说明第 n-1 个房子是蓝色或红色,此时到第 n 个房子的总花费为 \(min(blueDP[n-1], redDP[n-1]) + cost[n][2]\)
边界处理
很容易知道
redDP[0] = costs[0][0];
blueDP[0] = costs[0][1];
greenDP[0] = costs[0][2];
空间优化
因为每个数组的第 i 项只与第 i-1 项有关,所以只需要三个单变量分别表示第 i-1 项,循环滚动更新就行。
代码
dp
数组版
class Solution {
public int minCost(int[][] costs) {
if(costs.length == 0){
return 0;
}
int n = costs.length;
// 第 i 个房子刷成红色情况下的最小花费
int[] redDP = new int[n];
// 第 i 个房子刷成蓝色情况下的最小花费
int[] blueDP = new int[n];
// 第 i 个房子刷成绿色情况下的最小花费
int[] greenDP = new int[n];
redDP[0] = costs[0][0];
blueDP[0] = costs[0][1];
greenDP[0] = costs[0][2];
for(int i = 1; i < n; i++){
redDP[i] = Math.min(blueDP[i - 1], greenDP[i - 1]) + costs[i][0];
blueDP[i] = Math.min(redDP[i - 1], greenDP[i - 1]) + costs[i][1];
greenDP[i] = Math.min(blueDP[i - 1], redDP[i - 1]) + costs[i][2];
}
return Math.min(Math.min(redDP[n - 1], blueDP[n - 1]), greenDP[n - 1]);
}
}
空间优化版
class Solution {
public int minCost(int[][] costs) {
if(costs.length == 0){
return 0;
}
int n = costs.length;
int redCost = costs[0][0];
int blueCost = costs[0][1];
int greenCost = costs[0][2];
for(int i = 1; i < n; i++){
int newRedCost = Math.min(blueCost, greenCost) + costs[i][0];
int newBlueCost = Math.min(redCost, greenCost) + costs[i][1];
int newGreenCost = Math.min(redCost, blueCost) + costs[i][2];
redCost = newRedCost;
blueCost = newBlueCost;
greenCost = newGreenCost;
}
return Math.min(Math.min(redCost, blueCost), greenCost);
}
}