1. 程式人生 > >[LC難題必須要解決系列][之][DP] Paint House

[LC難題必須要解決系列][之][DP] Paint House

inpu pre 情況 can 最小 解決 循環 one posit

今天博主立下一個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;    
    }
}

[LC難題必須要解決系列][之][DP] Paint House