今天博主立下一个flag,必须要开始好好解决DP问题!!LC现在DP系列有117题,现在从易到难的顺序重走这117之路。
今天坐在Victoria coffee 里面从第一个最简单的paint house问题解决开始。
ps:这里都是博主自己做题时的思考过程,不是大神讲解,欢迎指正缺陷和错误。
Paint House
题目链接:https://leetcode.com/problems/paint-house/ There are a row of n houses, each house can be painted with one of the three colors: red, blue or green. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color. The cost of painting each house with a certain color is represented by a n x 3cost matrix. For example, costs[0][0] is the cost of painting house 0 with color red; costs[1][2] is the cost of painting house 1 with color green, and so on... Find the minimum cost to paint all houses. Note: All costs are positive integers. Example: Input: [[17,2,17],[16,16,5],[14,3,19]] Output: 10 Explanation: Paint house 0 into blue, paint house 1 into green, paint house 2 into blue. Minimum cost: 2 + 5 + 3 = 10.
拿到这道题,我首先想到的input 是一个二维数组,[[c1, c2,c3]] 比如这个代表的就是房子0号被染成c1是A[0][0] cost, 被染成c2 是A[0][1] cost, 被染成c3 是A[0][2] cost; 看到求min,我们当然可以全部清空列出来,用backsttracking去解决,但是我们可以想想这里每一步房子染成什么颜色和上一步是非常有关系的,比如,当前染成红色,那么前一个和后一个不能是红色,那我们可以想到状态序列型DP的一个想法,状态数组代表的意思就是当前这个房子如果染成某种颜色,整体能达到的最小cost是多少。 那么我们想假设我们用二维数组f[i][j] 来表示状态,那么情况就是前i个房子并且当前第i-1个房子染成颜色j所能使整体达到的最小cost是多少。那么最后一步就是前n个房子且最后第n-1个房子分别染成三种颜色的cost是多少。
状态方程式: f[i][j] = min{f[i-1][j-1], f[i-1][j+1]} + A[i-1][j] 其中j-1和j+1表示不同颜色。方程可以init为f[n+1][3];
初始:f[0][1] = A[o][1], f[0][2] = A[0][2], f[0][3] = A[0][3];
边界:没有边界,只是不同color情况。
计算顺序:f[0][j] .. f[2][j]...f[n][j], 最后取min{f[n][j]} j from 0 to x;
Time complexity: O(mnk);
class Solution { public int minCost(int[][] A) { if (A.length == 0) return 0; int m = A.length, n = A[0].length; int[][] f = new int[2][n]; int old = 0, now = 0; for (int i = 1; i <= m; i ++){ old = now; now = 1 - old; Arrays.fill(f[now], Integer.MAX_VALUE); for (int j = 0; j < n; j ++){ for (int k = 0; k < n; k ++){ if (k == j) continue; f[now][j] = Math.min(f[now][j], f[old][k] + A[i-1][j]); } } } int res = Integer.MAX_VALUE; for (int j = 0; j < n; j ++){ res = Math.min(res, f[now][j]); } return res; } }
Paint House II
分析思路其实和paint house I完全一样,代码如下:
TIme compexity: O(mk^2); m is the house number, k is color number
class Solution { public int minCostII(int[][] A) { if (A.length == 0 || A[0].length == 0) return 0; if (A[0].length == 1) return A[0][0]; int m = A.length, n = A[0].length; int[][] f = new int[2][n]; int old = 0, now = 0; for (int i = 1; i <= m; i ++){ old = now; now = 1 - now; // j represent what colors for (int j = 0; j < n; j ++){ f[now][j] = Integer.MAX_VALUE; // previous [i-1] colors for (int k = 0; k < n; k ++){ if (k == j) continue; f[now][j] = Math.min(f[now][j], A[i-1][j] + f[old][k]); } } } int min = Integer.MAX_VALUE; for (int j = 0; j < n; j ++){ min = Math.min(min, f[now][j]); } return min; } }
这里主要讲一个优化,因为我们每次要求f[i-1][j] j from 0 to k, 其中的最小值,我们可以求其中的两个最小值,这样在再一次循环0到k的时候,只需要看两个最小值即可。
class Solution { public int minCostII(int[][] A) { if (A.length == 0 || A[0].length == 0) return 0; if (A[0].length == 1) return A[0][0]; int m = A.length, n = A[0].length; int[][] f = new int[m+1][n]; for (int i = 1; i <= m; i ++){ // j represent what colors int a = -1, b = -1; for (int j = 0; j < n; j ++){ if (a == -1){ a = j; }else { if (f[i-1][j] < f[i-1][a]){ b = a; a = j; }else if (b == -1 || (b >= 0 && f[i-1][j] < f[i-1][b])){ b = j; } } } // As we only keep the max and 2nd max we can use this two only; for(int j = 0; j < n; j ++){ if (j == a){ f[i][j] = f[i-1][b] + A[i-1][j]; }else{ f[i][j] = f[i-1][a] + A[i-1][j]; } } } int min = Integer.MAX_VALUE; for (int j = 0; j < n; j ++){ min = Math.min(min, f[m][j]); } return min; } }