leetcode
leetcode 201 ~ 250
粉刷房子 II

粉刷房子 II

难度:

标签:

题目描述

代码结果

运行时间: 32 ms, 内存: 16.8 MB


/*
 * Leetcode: Paint House II
 * Problem statement: There are a row of n houses, each house can be painted with one of the k colors. 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 k cost matrix. 
 * Find the minimum cost to paint all houses.
 *
 * Approach: Dynamic Programming with Java Streams
 * 1. We will maintain a dp array where dp[i][j] represents the minimum cost to paint house i with color j.
 * 2. We will iterate through each house and each color, updating the dp array by adding the cost of painting the current house with the current color to the minimum cost of painting the previous house with a different color.
 * 3. The final answer will be the minimum value in the last row of the dp array.
 */
import java.util.Arrays;
import java.util.stream.IntStream;
 
public class SolutionStream {
    public int minCostII(int[][] costs) {
        if (costs == null || costs.length == 0) return 0;
        int n = costs.length;
        int k = costs[0].length;
        int[][] dp = new int[n][k];
 
        IntStream.range(0, k).forEach(j -> dp[0][j] = costs[0][j]);
 
        IntStream.range(1, n).forEach(i -> {
            IntStream.range(0, k).forEach(j -> {
                dp[i][j] = costs[i][j] + IntStream.range(0, k).filter(l -> l != j).map(l -> dp[i - 1][l]).min().orElse(Integer.MAX_VALUE);
            });
        });
 
        return IntStream.range(0, k).map(j -> dp[n - 1][j]).min().orElse(Integer.MAX_VALUE);
    }
}

解释

方法:

这个题解使用了动态规划的思路。定义 dp[j] 表示粉刷第 i 个房子时,第 i-1 个房子使用颜色 j 的最小花费。对于第 i 个房子,遍历每个颜色 j,若第 i-1 个房子使用最小花费的颜色不是 j,则 dp[j] = min_ + costs[i][j],否则 dp[j] = smin_ + costs[i][j],其中 min_ 和 smin_ 分别表示上一轮的最小花费和次小花费。然后更新当前轮的 min_、smin_、mind 和 smind。最后返回最后一轮的最小花费 min_ 即可。

时间复杂度:

O(nk)

空间复杂度:

O(k)

代码细节讲解

🦆
为什么在动态规划中使用两个变量min_和smin_来分别存储最小和次小花费,这样的设计有什么特别的优势?
在动态规划中使用min_和smin_存储最小和次小花费的主要优势在于处理状态转移时的效率。当更新当前房子的染色成本时,如果当前房子选择的颜色与前一个房子的最小成本颜色相同,我们需要使用次小成本来避免颜色重复。如果我们不提前记录次小值,每次计算时都需要重新遍历数组以寻找次小值,这将大大增加计算复杂度。通过记录下最小和次小值,我们可以在O(1)的时间复杂度内完成这一步,从而使整体算法效率更高。
🦆
在更新dp数组时,对于条件`j != mind`使用smin_而不是min_,这种设计的理由是什么?
这种设计的理由是为了避免颜色的重复使用。如果当前房子选择的颜色j与前一个房子的最小花费颜色相同(即j == mind),按照题目要求,不能选择相同颜色,因此这种情况下我们应该使用次小成本smin_来计算当前房子的成本。如果使用min_,则可能违反颜色不重复的规则,导致错误的计算结果。
🦆
算法中如何确保每次循环后min_和smin_总是代表当前所有颜色中的最小和次小花费?
算法通过在每个房子的颜色遍历过程中维护新的最小值new_min_和次小值new_smin_实现这一点。对于每种颜色的成本计算完成后,我们比较并更新new_min_和new_smin_。遍历完所有颜色后,将这些新计算的值赋值给min_和smin_,从而确保在下一轮计算中,min_和smin_正确地代表了所有颜色中的最小和次小花费。这个过程是在每一轮房子的颜色计算中重复进行的,确保了每次循环后的准确性。
🦆
在处理边界条件时,如何处理只有一个房子或一个颜色的情况?
在只有一个房子或一个颜色的情况下,算法简化如下:如果只有一个颜色,无论房子数量多少,每个房子只能使用这一种颜色,因此花费是所有房子该颜色成本的总和。如果只有一个房子,那么这个房子可以选择任何一种颜色中的最小成本。在程序实现时,这些情况自然会在遍历中处理,例如当只有一个颜色时,min_始终是这个颜色的成本,smin_无需考虑;当只有一个房子时,直接取这个房子所有颜色中的最小值即可。

相关问题

除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

请 不要使用除法,且在 O(n) 时间复杂度内完成此题。

 

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

 

提示:

  • 2 <= nums.length <= 105
  • -30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内

 

进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)

滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

 

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

 

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

粉刷房子

粉刷房子

栅栏涂色

栅栏涂色