最优硬币组合问题| 豆包MarsCode AI刷题

3 阅读2分钟

问题描述

小C有多种不同面值的硬币,每种硬币的数量是无限的。他希望知道,如何使用最少数量的硬币,凑出给定的总金额N。小C对硬币的组合方式很感兴趣,但他更希望在满足总金额的同时,使用的硬币数量尽可能少。

例如:小C有三种硬币,面值分别为 125。他需要凑出总金额为 18。一种最优的方案是使用三个 5 面值的硬币,一个 2 面值的硬币和一个 1 面值的硬币,总共五个硬币。


测试样例

样例1:

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

样例2:

输入:coins = [1, 3, 4], amount = 6
输出:[3, 3]

样例3:

输入:coins = [5], amount = 10
输出:[5, 5]

思路分析

这是一个典型的完全背包问题,可以使用动态规划来解决这个问题:

  1. 初始化一个数组 dp,长度为 amount + 1,每个位置初始化为 amount + 1(这个值是大于任何可能硬币数量的,作为初始状态),除了 dp[0],它应该初始化为0,因为凑出金额0不需要任何硬币。
  2. 遍历每种硬币的面值,对于每种面值,再遍历从该硬币面值到 amount 的所有金额,更新 dp 数组。
  3. 对于每个 dp[i],我们检查是否可以用当前面值的硬币来减少达到金额 i 所需的硬币数量。如果可以,则更新 dp[i] 为 min(dp[i], dp[i - coin] + 1)
  4. 完成动态规划数组后,我们可以通过回溯来找出使用了哪些硬币。从 dp[amount] 开始,如果 dp[i] 是由 dp[i - coin] + 1 得到的,那么我们就使用了面值为 coin 的硬币。

代码注释

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Collections;

public class Main {
    public static List<Integer> solution(int[] coins, int amount) {
        // dp数组,用于存储达到每个金额所需的最少硬币数量
        int[] dp = new int[amount + 1];
        // prev数组,用于记录达到每个金额时使用的最后一个硬币面值
        int[] prev = new int[amount + 1];
        // 初始化dp数组,所有值设置为amount + 1
        Arrays.fill(dp, amount + 1); 
        // 设置dp[0]为0,因为凑出金额0不需要任何硬币
        dp[0] = 0; 
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                // 如果当前硬币面值小于等于当前金额
                if (coins[j] <= i) {
                    // 如果使用当前硬币可以减少所需硬币数量
                    if (dp[i] > dp[i - coins[j]] + 1) {
                        dp[i] = dp[i - coins[j]] + 1; 
                        prev[i] = coins[j]; 
                    }
                }
            }
        }
        // 如果dp[amount]没有更新,说明无法凑出该金额
        if (dp[amount] == amount + 1) {
            return new ArrayList<>();
        }
        // 根据prev数组回溯找出硬币组合
        List<Integer> result = new ArrayList<>();
        for (int i = amount; i > 0; i -= prev[i]) {
            result.add(prev[i]);
        }
        // 将结果排序,确保面值大的硬币在前
        Collections.sort(result, Collections.reverseOrder());
        return result;
    }
    public static void main(String[] args) {
        System.out.println(solution(new int[]{1, 2, 5}, 18).equals(List.of(5, 5, 5, 2, 1)));
    }
}