[C++&&Rust]LeetCode No.1473 粉刷房子 III(每日一题)

原帖地址:http://blog.leanote.com/post/dawnmagnet/61cf7456274e

题目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NwOV7dOj-1620141680998)(https://leanote.com/api/file/getImage?fileId=60914f84ab6441786b01a2e1)]

思路分析

这道题总的来说值得上困难题。一开始看,一眼就看出是个dp,因为首先房子的数量太多,如果使用模拟(dfs)必定超时,而我之前说过,但凡是可以用dfs的地方就可以试试dp。
在这个问题中,给入了三个关键变量,m,n,target,这三个一看就是有大用处的(此处暗示三维dp),那么为什么使用三维dp而不是大家很好想到的二维dp呢,原因很简单,我们得在dp的系数上表示出遍历到了哪一个房子,这个房子的颜色可以有几种选择,这个房子与之前的一起构成了几个街区。这样一来就三个变量了。至于为什么第三个变量是“这个房子与之前的一起构成了几个街区”,原因很简单,其实dp的每个系数的选择都是有考虑的,每一个变量都尽可能地和前面已经遍历过的产生逻辑上的联系,这三个变量的选择都有联系,其中第一个和第三个的联系比较好看出来,而第二个颜色大家可能会想,并没什么联系,但是大家别忘了颜色对于街区的限制也是一个联系哦!
确定了三个系数的含义,再确定值的含义。因为题目要我们求最小值,那么我们就把值确定为最小值,这样遍历的时候也方便。
那么来到的最最重要的递推关系式。

DP的核心-递推关系

先确定最核心的递推逻辑。

// dp[i][j][n] m * n * target
// i房子,颜色j,已经有n个街区的最小cost
// 递推:dp[i + 1][jt][k + 1] min= dp[i][j][k] + cost[i + 1][jt] * (house[i + 1] != 0);
//       dp[i + 1][j][k] min= dp[i][j][k] + cost[i + 1][j] * (house[i + 1] != 0);

PS:这是我在写题的时候打的草稿,一定要打草稿,特别是DP,D到最后你自己都不知道你在D什么
这两句分别代表两种情况,一共是四种,分别是有无新增区块,是否可以涂颜色(house是否等于0)
这是最最核心的逻辑,前后两种颜色不一样的时候就会新增区块,house不等于0才需要加涂装,否则不能涂装(不是不想,是不能)
确定了核心逻辑之后,再仔细检查细枝末节。
在确定dp关系的时候,一定要精确的领会题目中每一句话的意思。确保每一句话对应的递推式和它所适用的情况都被推到了出来。
因为是表现最小值,我们在取初始值的时候就先按照INT_MAX(在rust中为i32::MAX)来表示非法值,也就是无法访问的情况。在处理时我们只处理合法的情况。
注意看题目,已经被涂过的房子不需要涂色。所以在判断房子时,先判断有没有颜色,有颜色就可以直接一步到位,不需要遍历该房颜色了。没有颜色则需要遍历该房颜色。
为了精简数组的元素,我们将表示街区的长度就设置为target,因为最小的街区数是1,我们就用0代替(节省空间And剪枝),所以说,当街区数目达到target-1时间到顶了,我们也不用再向>=target的方向遍历,对于答案来说没有意义。那么又要分好多种情况,因为有的情况是增加区块的,有的情况是不增加区块的。所以又要检查。(这就是这道题的困难之处,就是非常复杂)

Rust代码

impl Solution {
    pub fn min_cost(houses: Vec<i32>, cost: Vec<Vec<i32>>, m: i32, n: i32, target: i32) -> i32 {
        let m = m as usize;
        let n = n as usize;
        let target = target as usize;
        let mut dp = vec![vec![vec![i32::MAX; target]; n]; m];
        if houses[0] != 0{
            dp[0][houses[0] as usize - 1][0] = 0;
        } else {
            for color in 0..n {
                dp[0][color][0] = cost[0][color];
            }
        }
        for i in 0..(m - 1) {
            for j in 0..n {
                for k in 0..target {
                    if dp[i][j][k] != i32::MAX {
                        if houses[i + 1] == 0 {
                            if k != target - 1 {
                                for jt in 0..n {
                                    if jt != j {
                                        dp[i + 1][jt][k + 1] = dp[i + 1][jt][k + 1].min(dp[i][j][k] + cost[i + 1][jt]);
                                    }
                                }
                            }
                            dp[i + 1][j][k] = dp[i + 1][j][k].min(dp[i][j][k] + cost[i + 1][j]);
                        } else {
                            let jt = (houses[i + 1] - 1) as usize;
                            if jt != j && k != target - 1 {
                                dp[i + 1][jt][k + 1] = dp[i + 1][jt][k + 1].min(dp[i][j][k]);
                            }
                            if jt == j {
                                dp[i + 1][jt][k] = dp[i + 1][jt][k].min(dp[i][j][k]);
                            }
                        }
                    }
                }
            }
        }
        let mut res = i32::MAX;
        for j in 0..n {
            res = res.min(dp[m - 1][j][target - 1]);
        }
        if res == i32::MAX {
            -1
        } else {
            res
        }
    }
}

C++代码

class Solution {
public:
    int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
        int dp[m][n][target];
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                for (int k = 0; k < target; ++k)
                    dp[i][j][k] = INT_MAX;
        if (houses[0] != 0) {
            dp[0][houses[0] - 1][0] = 0;
        } else {
            for (int color = 0; color < n; ++color) {
                dp[0][color][0] = cost[0][color];
            }
        }
        for (int i = 0; i < m - 1; ++i) {
            for (int j = 0; j < n; ++j) {
                for (int k = 0; k < target; ++k) {
                    if (dp[i][j][k] != INT_MAX) {
                        if (houses[i + 1] == 0) {
                            if (k != target - 1) {
                                for (int jt = 0; jt < n; ++jt) {
                                    if (jt != j) {
                                        dp[i + 1][jt][k + 1] = min(dp[i + 1][jt][k + 1], dp[i][j][k] + cost[i + 1][jt]);
                                    }
                                }
                            }
                            dp[i + 1][j][k] = min(dp[i + 1][j][k], dp[i][j][k] + cost[i + 1][j]);
                        } else {
                            int jt = (houses[i + 1] - 1);
                            if (jt != j && k != target - 1) {
                                dp[i + 1][jt][k + 1] = min(dp[i + 1][jt][k + 1], dp[i][j][k]);
                            }
                            if (jt == j) {
                                dp[i + 1][jt][k] = min(dp[i + 1][jt][k], dp[i][j][k]);
                            }
                        }
                    }
                }
            }
        }
        int res = INT_MAX;
        for (int j = 0; j < n; ++j) {
            res = min(res, dp[m - 1][j][target - 1]);
        }
        if (res == INT_MAX) {
            return -1;
        } else {
            return res;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值