LeetCode 第 322 题:零钱兑换(动态规划)

地址:https://leetcode-cn.com/problems/coin-change/

注意两件事情:

1、dp 的值有上限,因为都是整数,因此是 amount + 1
2、从可以转移的地方开始计算即可;
3、和完全背包问题产生联系。

方法一:动态规划

使用最基本的“动态规划”的思路。

第 1 步:状态定义

dp[i] 表示凑成金额为 i 的最少硬币数。

第 2 步:状态转移方程

根据题目示例 1:coins = [1, 2, 5]amount = 11。很容易想到:

总金额 11 ,根据可以选择的硬币列表 [1, 2, 5] 可以这样拆解:

  • 11 = 1 + (11 - 1):表示 11 可以拆成面值为 1 的 1 个硬币和凑成面值为 10 的最小硬币数之和;
  • 11 = 2 + (11 - 2):表示 11 可以拆成面值为 2 的 1 个硬币和凑成面值为 9 的最小硬币数之和;
  • 11 = 5 + (11 - 5):表示 11 可以拆成面值为 5 的 1 个硬币和凑成面值为 6 的最小硬币数之和;

三者取最小值。下面写出状态转移方程:

d p [ i ] = min { 1 + d p [ i c o i n s [ j ] ] } dp[i] = \min \{1 + dp[i - coins[j]]\}

这里 j 是硬币列表的序号。注意 i - coins[j] 必须大于等于 0 ,才有意义。

此外,需注意:有两个非常特殊的状态:

1、dp[0]0 这个面值是一个在填表的时候被参考的数值,如果总面值为 5,刚好有个硬币的面值也为 5 ,此时 dp[5] = 1 + dp[5 - 5] = 1,故设置 dp[0] = 1

2、 也有可能出现例 2 的情况,此时需要设置 dp[i] = - 1,这道题问的又是最小值,因此,初始化的时候不能设置为 0 ,应该设置为一个足够大的数。

第 3 步:初始化

(上面已经讨论过了)

第 4 步:输出

输出 dp[amount] 即可。

第 5 步:是否可以状态压缩

每一步都有可能用到之前的状态,因此不能压缩。

再次提醒自己:初始化的时候,因为要比较的是最小值,因此不能初始化为 1 -1 。并且这里 1 -1 有专门的含义。

Java 代码:

import java.util.Arrays;

public class Solution {

    public int coinChange(int[] coins, int amount) {
        // 给 0 占位
        int[] dp = new int[amount + 1];

        // 注意:因为要比较的是最小值,这个不可能的值就得赋值成为一个最大值
        Arrays.fill(dp, amount + 1);

        dp[0] = 0;

        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (i - coin >= 0 && dp[i - coin] != -1) {
                    dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
                }
            }

            if (dp[i] == amount + 1) {
                dp[i] = -1;
            }
        }

        if (dp[amount] == amount + 1) {
            dp[amount] = -1;
        }
        return dp[amount];
    }
}

可以用“完全背包”的模板。(代码是正确的,原因我还没有想得特别清楚。)

import java.util.Arrays;

public class Solution {

    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;

        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }

        if (dp[amount] == 9999) {
            dp[amount] = -1;
        }
        return dp[amount];
    }
}

注意:1、初始化的时候,赋值成一个很大的数就可以了,int 的最大值就有可能造成越界;

2、不要在代码中做打印输出,会拖慢时间。

发布了442 篇原创文章 · 获赞 330 · 访问量 123万+

猜你喜欢

转载自blog.csdn.net/lw_power/article/details/103872888