leetcode
leetcode 1351 ~ 1400
粉刷房子 III

粉刷房子 III

难度:

标签:

题目描述

代码结果

运行时间: 72 ms, 内存: 19.1 MB


/*
 * This solution uses Java Streams to solve the problem of painting houses to form a specific number of blocks with minimum cost.
 * We leverage the same dynamic programming approach but implement it in a more functional style with Streams.
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public int minCost(int[] houses, int[][] cost, int m, int n, int target) {
        int MAX_COST = 1000001;
        int[][][] dp = new int[m][target + 1][n + 1];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j <= target; j++) {
                Arrays.fill(dp[i][j], MAX_COST);
            }
        }
        IntStream.range(1, n + 1).forEach(k -> {
            if (houses[0] == 0) {
                dp[0][1][k] = cost[0][k - 1];
            } else if (houses[0] == k) {
                dp[0][1][k] = 0;
            }
        });
        IntStream.range(1, m).forEach(i -> {
            IntStream.range(1, target + 1).forEach(j -> {
                IntStream.range(1, n + 1).forEach(k -> {
                    if (houses[i] != 0 && houses[i] != k) {
                        return;
                    }
                    int currentCost = houses[i] == 0 ? cost[i][k - 1] : 0;
                    IntStream.range(1, n + 1).forEach(kPrev -> {
                        if (k == kPrev) {
                            dp[i][j][k] = Math.min(dp[i][j][k], dp[i - 1][j][kPrev] + currentCost);
                        } else {
                            dp[i][j][k] = Math.min(dp[i][j][k], dp[i - 1][j - 1][kPrev] + currentCost);
                        }
                    });
                });
            });
        });
        return IntStream.range(1, n + 1)
                         .map(k -> dp[m - 1][target][k])
                         .min()
                         .orElse(MAX_COST) == MAX_COST ? -1 : IntStream.range(1, n + 1).map(k -> dp[m - 1][target][k]).min().orElse(MAX_COST);
    }
}

解释

方法:

这个题解使用了动态规划的方法。状态定义为 dp[i][t][j],表示前 i+1 个房子构成 t+1 个街区,且第 i 个房子颜色为 j+1 时的最小花费。初始状态处理第一个房子的颜色和花费。对于每个房子,如果未涂色,考虑涂不同颜色的花费;如果已涂色,则直接更新花费。计算时,保持两个最小值(min_idx 和 min_idx_),用于优化时间复杂度,避免重复计算。最后返回所有情况中的最小花费,如果不存在有效方案则返回 -1。

时间复杂度:

O(m * target * n * n)

空间复杂度:

O(m * target * n)

代码细节讲解

🦆
为什么在处理第一个房子的初始化状态时,只有当houses[0]为0时才从cost数组中取值,已涂色的房子花费为0而不是其实际花费?
在这个问题的上下文中,当`houses[0]`为0,意味着第一个房子尚未被涂色,因此需要从`cost`数组中选择一个颜色来涂色,并且支付相应的费用。而如果`houses[0]`不为0,表示第一个房子已经预先涂有一种颜色,这种情况下不需要再支付涂色费用,因此初始化花费为0。这种设定是基于只考虑额外的涂色费用,预设的颜色被视为无需额外费用的初始状态。
🦆
动态规划中的状态转移方程是如何确保只形成target个街区的,能详细解释下其中的逻辑吗?
状态转移方程通过控制`target`个街区的形成来确保正确的街区数量。在动态规划中,`t`表示已形成的街区数量。当我们考虑第`i`个房子时,如果将其涂成与前一个房子不同的颜色,则街区数量增加(即`t+1`),并且在`dp[i][t][j]`的计算中使用`dp[i-1][t-1][j]`的值;如果颜色与前一个房子相同,则街区数量不变,使用`dp[i-1][t][j]`来更新。通过这种方式,算法确保最终形成的街区数量正好是`target`。
🦆
在代码中,min_t和max_t的计算公式是如何得出的,这两个值分别代表什么含义?
在代码中,`min_t`和`max_t`用于确定在处理第`i`个房子时可能的街区数的范围。`min_t`表示在考虑到剩余房子数量后能形成的最小街区数,它确保了即使每个后续房子都形成一个新的街区,也不会超过`target`个街区。`max_t`表示考虑到当前房子位置时能形成的最大街区数,它确保不会因为房子数量不足而无法形成足够的街区。这两个值的计算保证了动态规划在合理的街区数范围内进行,避免无效的计算。
🦆
你是如何处理涂色成本计算中的两个最小值(min_idx 和 min_idx_)的?这种处理方法有什么特别的考量吗?
在处理涂色成本时,使用`min_idx`和`min_idx_`是为了优化时间复杂度并减少冗余计算。`min_idx`存储当前最小花费的颜色索引,而`min_idx_`存储次小花费的颜色索引。这样,在更新当前房子的颜色成本时,如果当前房子选择的是最小花费颜色,则可以使用次小花费来计算新的成本;如果不是,则使用最小花费。这种方法有效避免了在每次更新时重新计算所有颜色的最小和次小成本,提高了效率。

相关问题