【線性 dp】A004_LC_給房子塗色 III(三維記憶化 / dp)

一、Problem

There is a row of m houses in a small city, each house must be painted with one of the n colors (labeled from 1 to n), some houses that has been painted last summer should not be painted again.

A neighborhood is a maximal group of continuous houses that are painted with the same color. (For example: houses = [1,2,2,3,3,2,1,1] contains 5 neighborhoods [{1}, {2,2}, {3,3}, {2}, {1,1}]).

Given an array houses, an m * n matrix cost and an integer target where:

houses[i]: is the color of the house i, 0 if the house is not painted yet.
cost[i][j]: is the cost of paint the house i with the color j+1.
Return the minimum cost of painting all the remaining houses in such a way that there are exactly target neighborhoods, if not possible return -1.

Input: houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3
Output: 9
Explanation: Paint houses of this way [1,2,2,1,1]
This array contains target = 3 neighborhoods, [{1}, {2,2}, {1,1}].
Cost of paint all houses (1 + 1 + 1 + 1 + 5) = 9.

Constraints:

m == houses.length == cost.length
n == cost[i].length
1 <= m <= 100
1 <= n <= 20
1 <= target <= m
0 <= houses[i] <= n
1 <= cost[i][j] <= 10^4

二、Solution

方法一:記憶化搜索

不要被題目過多的條件弄昏頭腦,整理一下信息其實講的是:給 m 個房子塗上色,然後讓相同顏色若干房子形成一個街道,用盡量少的代價去將 m 個房子填圖顏色,最後形成 target 個街道。

  • 已經被填塗顏色的房子不能再被填塗,這句話告訴我們:house[i] != 0 時,cost[i][house[i]-1] = 0,而其它顏色可初始化爲 INF,這樣就可以避免將房子 ii 塗成其它顏色。
  • dfs(i,j,k)dfs(i, j, k) 表示將前 ii 個房子塗成顏色 kk 形成 jj 個街道的最少花費。
  • 對於第 ii 個房子,我們可以選擇填塗爲顏色 kk,也可以塗成其它顏色:
    • 塗成顏色 k,那麼街區劃分數量不變
    • 塗成其它顏色,那麼街區劃分數量 - 1
class Solution {
	int n, INF = 0x3f3f3f3f, cs[][], f[][][];
	int dfs(int i, int j, int k) {
		if (j == 0 || j > i + 1) return INF;
		if (i == 0) 			 return cs[0][k];
		if (f[i][j][k] != 0)     return f[i][j][k];
		int min = INF;
		for (int c = 0; c < n; c++)  if (c != k) {
			min = Math.min(min, cs[i][k] + dfs(i-1, j-1, c));
		}
		min = Math.min(min, dfs(i-1, j, k) + cs[i][k]);
		return f[i][j][k] = min;
	}
    public int minCost(int[] hs, int[][] cost, int m, int n, int tar) {
        cs = cost;
        this.n = n;
        int min = INF;
        for (int i = 0; i < hs.length; i++) {
            if (hs[i] != 0) {
                Arrays.fill(cs[i], INF);
    	        cs[i][hs[i]-1] = 0;
            }
        }
        f = new int[m][tar+1][n];
        for (int c = 0; c < n; c++) 
        	min = Math.min(min, dfs(m-1, tar, c));
        return min == INF ? -1 : min;
    }
}

複雜度分析

  • 時間複雜度:O(m×n2)O(m × n^2)
  • 空間複雜度:O(m×tar×n)O(m × tar × n)

因爲遞歸是自頂向下的,所以只有遇到邊界纔會逐層返回,自行對比下與 dp 的異同


方法二:dp

  • 定義狀態
    • f[i][j][k]f[i][j][k] 表示將前 ii 個房子塗成顏色 kk 形成 jj 個街道的最少花費
  • 思考初始化:
    • f[0][1][c]=cost[i][c]f[0][1][c] = cost[i][c] 將第 0 個房子塗成顏色 c 形成 1 個街道時的最小花費只能是 cost[i][c]cost[i][c]
  • 思考狀態轉移方程
    • for (c in n),f[i][j][k] = min(f[i-1][j][k] + cost[i][k], f[i][j-1][k] + cost[i][c] 對於第 ii 個房子,可如果塗成顏色與前 i1i-1 個房子相同的顏色 kk ,然後不劃分當前街區;也可以塗成與第 i1i-1 房子不同的顏色,然後劃分新街區。
  • 思考輸出f[m1][tar][0...n]f[m-1][tar][0...n] 表示第 m 間房子塗成顏色 k[0,n)k∈[0,n) 的最小花費
class Solution {
    public int minCost(int[] hs, int[][] cs, int m, int n, int tar) {
    	int INF = 0x3f3f3f3f;
    	for (int i = 0; i < m; i++) if (hs[i] != 0) {
    		Arrays.fill(cs[i], INF);
    		cs[i][hs[i]-1] = 0;
        }
		int res = INF, f[][][] = new int[m][tar+1][n];
		for (int i = 0; i < m; i++)
		for (int j = 0; j <= tar; j++)
		for (int k = 0; k < n; k++)
            f[i][j][k] = INF;

		for (int k = 0; k < n; k++)
			f[0][1][k] = cs[0][k];

		for (int i = 1; i < m; i++)
		for (int j = 1; j <= Math.min(i+1, tar); j++)
		for (int k = 0; k < n; k++) {
			int min = INF;
			for (int c = 0; c < n; c++) if (c != k) {
				min = Math.min(min, f[i-1][j-1][c] + cs[i][k]);
			}
			f[i][j][k] = Math.min(min, f[i-1][j][k] + cs[i][k]);
		}
		for (int k = 0; k < n; k++) if (f[m-1][tar][k] < res) {
            res = f[m-1][tar][k];
		}
		return res == INF ? -1 : res;
    }
}

複雜度分析

  • 時間複雜度:O(m×tar×n2)O(m × tar × n^2)
  • 空間複雜度:O(...)O(...)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章