题目描述
假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
提示:
costs.length == n
costs[i].length == 3
1 <= n <= 100
1 <= costs[i][j] <= 20
示例 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
答案
class Solution {
public int minCost(int[][] costs) {
int[][] dp = new int[costs.length][3];
dp[0] = costs[0];
for(int i =1; i<costs.length; i++) {
dp[i][0] = Math.min(costs[i][0]+dp[i-1][1], costs[i][0]+dp[i-1][2]);
dp[i][1] = Math.min(costs[i][1]+dp[i-1][0], costs[i][1]+dp[i-1][2]);
dp[i][2] = Math.min(costs[i][2]+dp[i-1][0], costs[i][2]+dp[i-1][1]);
}
return Math.min(Math.min(dp[costs.length-1][0],dp[costs.length-1][1]), dp[costs.length-1][2]);
}
}
分析
简单分析一下,这道题目符合动态规划题目的特点
无后效性 当某个房子选择了某颜色后,不会受到后面房子选择的影响
最优子结构 当已知粉刷前 i 个房子的最小花费成本时,根据粉刷第 i + 1号房子的花费成本可以计算粉刷前 i 个房子的最小花费成本+当前房子选择后的成本
重叠子问题 我们可以看到在迭代中,为某个房子选择了某个颜色后,需要重新计算前面一个房子选择不同颜色的成本,这里面存在大量的重复计算
现在来看看怎么求解:
1)确定初始化状态,初始化状态作为整个求解链路的原点,需要优先明确;
2)状态参数,中间状态在一步一步推导出最终状态的过程中会发生变化的变量;
3)明确决策方式,即:如何通过前面的状态推导出后面的状态;
4)中间状态存储,子问题存在大量重复计算的情况,我们将中间状态存储入“备忘录”。
初始状态:题目描述最少有一个房子,那原点就是只有一套房子的情况下的最小成本min(cost[0][0], cost[0][1], cost[0][2])
状态参数:房子索引,随着房子一套一套的遍历、决策,总成本、和房子索引一直在变化,总成本是我们的最终求解目标,状态参数即为房子索引
决策方式: 对于每一套房子我们都要三个颜色可选,但是题目中有一个限定条件,相邻的两个房子颜色不能相同 因此我们需要结合前一套房子的选择得出当前房子选择后的最小成本。
举例:
当i=2时,即求解第二套房子的最小成本的时候,我们可以枚举,当第二套房子选择红色,那么选择红色总成本就是第一套房子的蓝色或绿色成本加上第二套房子的红色成本,在这里面取最小值,第二套的绿色蓝色可以类推。
中间状态存储:我们需要储存下前面的房子选择任意颜色的最小成本,帮助我们决策后面的房子选择什么颜色成本最小。
状态转移方程可以是这样的:
dp[i][0]=min(dp[i−1][1],dp[i−1][2])+costs[i][0]
dp[i][1]=min(dp[i−1][0],dp[i−1][2])+costs[i][1]
dp[i][2]=min(dp[i−1][0],dp[i−1][1])+costs[i][2]
也可以
dp[i][j]=min(dp[i−1][(j+1)mod3],dp[i−1][(j+2)mod3])+costs[i][j]
如果觉得本文有帮助可以分享给自己的小伙伴们!邀请他们关注下我的微信公众号
小哥爱code