322. 零钱兑换 (动态规划)

LeetCode - 322. 零钱兑换


题目描述

难度:中等

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

示例 4:

输入:coins = [1], amount = 1
输出:1

示例 5:

输入:coins = [1], amount = 2
输出:2

提示:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

分析

本题可以使用动态规划来完成,不得不说,动态规划刚开始学会一点皮毛的时候想用在题目上是真的难;

定义状态,dp[i] 表示总金额为 i 的情况下,需要的硬币最小数量;

初始化值 dp[0] = 0,总金额为 0 的时候不用消耗一枚硬币;

题目给定 coins 代表所有面值的硬币,我们用 coin 来代表 coins 中任意一面值;
对于总金额 i 来说,当 i >= coin 时,有dp[i] = min(dp[i - coin]) + 1

也就是说,dp[i] 是从所有只需要一枚硬币就可以达到 i 这个金额的金额数中取需要硬币数最少的金额;

举例例子解释一下,有 coins {1,2,3},amount = 6,当我们计算 dp[6] 的时候,是取 dp[6-1] dp[6-2] dp[6-3] 这三个值中最小的为结果然后加 1 就得到了 dp[6] 的所需要的硬币个数;
因为上面这三个值都只需要一枚硬币就可以达到 6 这个金额,而显而易见 dp[3] 需要的硬币最少,它只需要一枚,所以dp[6] 所消耗的硬币 是 dp[3] + 1 = 2;

所以有状态转移方程 dp[i] = min(dp[i - coin]) + 1,coin ∈ coins;

代码:

public static int coinChange(int[] coins, int amount) {
        if(amount == 0){
            return 0;
        }
        int[] dp = new int[amount + 1];  // 状态

        for (int i = 1; i < dp.length; i++){
            dp[i] = Integer.MAX_VALUE - 1;  // 定义每个金额所需硬币初始值
            for (int coin : coins) {
                if(i >= coin){   // 当满足 i >= coin 时,才能使用用该硬币
                    dp[i] = Math.min(dp[i - coin] + 1 , dp[i]);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount]; // 当硬币初始值未改变时,代表该硬币不能凑齐
    }

总结

动态规划真难,就算写出来了,还是一愣一愣的,总感觉有点懵,特别是状态转移方程真的难想,再接再厉吧,每日充电!



岁月悠悠,衰微只及肌肤;热忱抛却,颓废必致灵魂

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值