1473. 粉刷房子 III

163 阅读3分钟

这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战

1473. 粉刷房子 III

在一个小城市里,有 m 个房子排成一排,你需要给每个房子涂上 n 种颜色之一(颜色编号为 1 到 n )。有的房子去年夏天已经涂过颜色了,所以这些房子不需要被重新涂色。

我们将连续相同颜色尽可能多的房子称为一个街区。(比方说 houses = [1,2,2,3,3,2,1,1] ,它包含 5 个街区 [{1}, {2,2}, {3,3}, {2}, {1,1}] 。)

给你一个数组 houses ,一个 m * n 的矩阵 cost 和一个整数 target ,其中:

houses[i]:是第 i 个房子的颜色,0 表示这个房子还没有被涂色。 cost[i][j]:是将第 i 个房子涂成颜色 j+1 的花费。 请你返回房子涂色方案的最小总花费,使得每个房子都被涂色后,恰好组成 target 个街区。如果没有可用的涂色方案,请返回 -1 。

示例 1:

输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3 输出:9 解释:房子涂色方案为 [1,2,2,1,1] 此方案包含 target = 3 个街区,分别是 [{1}, {2,2}, {1,1}]。 涂色的总花费为 (1 + 1 + 1 + 1 + 5) = 9。 示例 2:

输入:houses = [0,2,1,2,0], cost = [[1,10],[10,1],[10,1],[1,10],[5,1]], m = 5, n = 2, target = 3 输出:11 解释:有的房子已经被涂色了,在此基础上涂色方案为 [2,2,1,2,2] 此方案包含 target = 3 个街区,分别是 [{2,2}, {1}, {2,2}]。 给第一个和最后一个房子涂色的花费为 (10 + 1) = 11。 示例 3:

输入:houses = [0,0,0,0,0], cost = [[1,10],[10,1],[1,10],[10,1],[1,10]], m = 5, n = 2, target = 5 输出:5 示例 4:

输入:houses = [3,1,2,3], cost = [[1,1,1],[1,1,1],[1,1,1],[1,1,1]], m = 4, n = 3, target = 3 输出:-1 解释:房子已经被涂色并组成了 4 个街区,分别是 [{3},{1},{2},{3}] ,无法形成 target = 3 个街区。

解题思路

数组dp[i][j][k]中存储的元素含义是前i座房子并且第i座房子的颜色是j并且街区数目为k时,产生的最小花费

代码解读

1.这段代码作用是将房子的颜色从0开始编号,使得后面颜色的遍历直接从0开始

        for (int i = 0; i < houses.length; i++) {
            houses[i]--;
        }

2.这段代码作用是初始化dp数组,将全部元素置为最大值,因为题目需要选出的时最小值,因此没有填入元素的位置即为最大值,后面在比较的时候就会失效

        for (int i = 0; i <m ; i++) {
            for(int j=0;j<n;j++)
                Arrays.fill(dp[i][j],max);
        }

3.状态转移

        for (int i = 0; i <m ; i++) {//遍历所有房子
            for(int j=0;j<n;j++){//遍历所有颜色
                if(houses[i]!=-1&&houses[i]!=j)  continue;
//条件等价于houses[i]==-1||houses[i]==j,就直接下一轮循环
//houses[i]==-1代表房子没上色,houses[i]==j代表当前房子已经上色并且颜色是j,因为颜色已经固定了,所以
//dp[i][j][k]只能在j==houses[i]的情况下填入元素,颜色j等于其他值的情况是不可能出现


                for (int k = 0; k < target; k++) {//遍历街区个数
                    for (int p = 0; p < n; p++) {//遍历前一个房子的颜色
                    //先分为两种情况 
                    //情况一:前一个房子和当前房子同色
                    //情况二:前一个房子和当前房子不同色   


                        if (p==j)//情况一:前一个房子和当前房子同色
                        {
                            if(i==0)//1.当前房子是第一家,因此不能从前一座房子转移状态
                            {
                             if(k==0)//当前街区数只可能为0,因为当前只有一家房子,不可能产生k>0 
                                    dp[i][j][k]=0;

                            }else{//2.当前房子不是第一家
                                dp[i][j][k]= Math.min(dp[i][j][k],dp[i-1][j][k]);
                //因为前一个房子和当前房子同色,因此街区数是没有增加的,颜色也是和前面房子的一样
                            }
                        }else if(i>0&&k>0){//情况二:前一个房子和当前房子不同色
                            dp[i][j][k]= Math.min(dp[i][j][k],dp[i-1][p][k-1]);
                //因为当前房子和前一个房子,颜色不一样,因此需要产生一个新的街区
                        }
                    }
                    if(houses[i]==-1&&dp[i][j][k]!=max)
//houses[i]==-1代表房子没上色,dp[i][j][k]!=max 代表从前面的循环中是不能向dp[i][j][k]填入元素的
//例如 dp[0][j][k](k>1) 这种情况是不可能出现的,因此不能填入元素,也不能在计算这种情况的花费
                        dp[i][j][k]+=cost[i][j];
                }
            }
        }

4.dp[m-1][i][target-1],m-1代表所有房子(前m座房子),target-1代表有target个街区,i是最后一个房子的颜色,找出最小值。

        for (int i = 0; i < n; i++) {
            res= Math.min(res,dp[m-1][i][target-1]);
        }

代码

class Solution {
    static int max=Integer.MAX_VALUE/2;
    public int minCost(int[] houses, int[][] cost, int m, int n, int target) {
    int[][][] dp=new int[m][n][target];
        for (int i = 0; i < houses.length; i++) {
            houses[i]--;
        }
        for (int i = 0; i <m ; i++) {
            for(int j=0;j<n;j++)
                Arrays.fill(dp[i][j],max);
        }
        for (int i = 0; i <m ; i++) {
            for(int j=0;j<n;j++){
                if(houses[i]!=-1&&houses[i]!=j)  continue;
                for (int k = 0; k < target; k++) {
                    for (int p = 0; p < n; p++) {
                        if (p==j)
                        {
                            if(i==0)
                            {
                             if(k==0)   dp[i][j][k]=0;
                            }else{
                                dp[i][j][k]= Math.min(dp[i][j][k],dp[i-1][j][k]);
                            }
                        }else if(i>0&&k>0){
                            dp[i][j][k]= Math.min(dp[i][j][k],dp[i-1][p][k-1]);
                        }
                    }
                    if(houses[i]==-1&&dp[i][j][k]!=max)
                        dp[i][j][k]+=cost[i][j];
                }
            }
        }
        int res=max;
        for (int i = 0; i < n; i++) {
            res= Math.min(res,dp[m-1][i][target-1]);
        }
        return res==max?-1:res;
    }
}