1068. Find More Coins (30)

本文介绍了一种使用动态规划解决背包问题的方法,特别是针对支付场景下寻找特定面额组合的问题。通过构建状态方程并利用降序排列的硬币面额数组,确保找到满足支付条件的最小面额序列。

Eva loves to collect coins from all over the universe, including some other planets like Mars.  One day she visited a universal shopping mall which could accept all kinds of coins as payments.  However, there was a special requirement of the payment: for each bill, she must pay the exact amount.  Since she has as many as 104 coins with her, she definitely needs your help.  You are supposed to tell her, for any given amount of money, whether or not she can find some coins to pay for it.

Input Specification:

Each input file contains one test case.  For each case, the first line contains 2 positive numbers: N (<=104, the total number of coins) and M(<=102, the amount of money Eva has to pay).  The second line contains N face values of the coins, which are all positive numbers.  All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in one line the face values V1 <= V2 <= ... <= Vk such that V1 + V2 + ... + Vk = M.  All the numbers must be separated by a space, and there must be no extra space at the end of the line.  If such a solution is not unique, output the smallest sequence.  If there is no solution, output "No Solution" instead.

Note: sequence {A[1], A[2], ...} is said to be "smaller" than sequence  {B[1], B[2], ...} if there exists k >= 1 such that A[i]=B[i] for all i < k, and A[k] < B[k].

Sample Input 1:
8 9
5 9 8 7 2 3 4 1
Sample Output 1:
1 3 5
Sample Input 2:
4 8
7 2 4 3
Sample Output 2:

No Solution

思路:典型的背包问题,用DP做,关键是要找出状态方程:

(1)DP[ i ][ j ] = max { DP[ i-1] [ j ] , DP[ i-1 ][ j - price[ i ] ] + price[ i ] }

简单解释:max中的两个参数表示的含义:

     1、价格j 无法用包含price[ i ]表示,用前i-1枚硬币表示   2、可以有price[ i ]用前i-1枚表示价格j-price[ i ],再加入price[ i ]刚好够!

其中price[ i ]为第 i 枚硬币的值 其中 price[ i ]经过排序,为降序数组

这里的DP[ i ][ j ]表示i 枚硬币能表示的<=j的最大值 其中 代码中的hasIt[ i ][ j ]用来记录DP[ i ][ j ]中是否包括price[ i ](这里 j 表示价格)

AC参考代码:

#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
int price[10001];
int priceArr[10001][101];
int hasIt[10001][101];  //标记priceArr[i][j]中是否包括price[i]项
int M,N;
int compare(int a,int b){
    return a>b;
}
int main()
{
    //ifstream cin("1.txt");
    cin>>N>>M;
    int index = 1;  //记录符合条件的硬币的种类个数
    for(int i=0;i<N;i++){
        int temp = 0;
        cin>>temp;
        if(temp<=M){       //过滤那些值大于M的值
            price[index++] = temp;
        }
    }
    sort(price+1,price+index,compare);  //因为最后的输入要按照字典排序,所以这边按照降序排序

    for(int i=1;i<index;i++){
        for(int j=1;j<=M;j++){
            if(j>=price[i]){    //到达<=j时的最大值可以包括price[i]
                if(priceArr[i-1][j]>priceArr[i-1][j-price[i]]+price[i]){    //包括price[i]比没包括要小
                    priceArr[i][j] = priceArr[i-1][j];
                    hasIt[i][j] = 0;    //标记没有包括price[i]
                }else{
                    priceArr[i][j] = priceArr[i-1][j-price[i]]+price[i];
                    hasIt[i][j] = 1;    //包括price[i]
                }
            }else{
                priceArr[i][j] = priceArr[i-1][j];
                hasIt[i][j] = 0;    //标记没有包括price[i]
            }
        }
    }
    if(priceArr[index-1][M]!=M){    //输入的硬币种类无法准确满足支付的M的值
        cout<<"No Solution";
        return 0;
    }else{
        while(M>0){
            if(hasIt[index-1][M]){  //从index-1向前遍历只要hasIt标记为1的就输出
                cout<<price[index-1];
                M -= price[index-1];
                if(M>0)
                    cout<<" ";
            }
            index--;
        }
    }
    return 0;
}


 

你提供的代码片段是一个试图实现**零钱兑换(Coin Change)**问题的函数,但目前只写了前半部分,并未完成逻辑。目标是:**给定不同面值的硬币 coins 和一个总金额 amount,计算出兑换 amount 所需的最少硬币数**。 --- ## 🚫 当前代码的问题分析: ```java public int coinChange(int[] coins, int amount) { List<Integer> list = Arrays.stream(coins).sorted().collect(Collectors.toList()); } ``` ### ❌ 问题 1:代码未完成 - 函数没有返回值,Java 编译器会报错。 - 没有实现动态规划或递归等核心逻辑。 ### ❌ 问题 2:排序无意义 - `Arrays.stream(coins).sorted().collect(Collectors.toList())` 是对硬币排序,但在动态规划中并不需要排序。 - 排序对零钱兑换问题(最少硬币数)没有帮助,除非是贪心策略,但贪心策略在本题中**不适用**(因为硬币面额不一定是能用贪心解决的)。 --- ## ✅ 正确实现(使用动态规划) 下面是使用 **动态规划** 实现的完整 `coinChange` 方法,可以正确求解该问题。 ```java import java.util.Arrays; public class Solution { public int coinChange(int[] coins, int amount) { // 创建一个 dp 数组,dp[i] 表示组成金额 i 所需的最少硬币数 int[] dp = new int[amount + 1]; Arrays.fill(dp, amount + 1); // 初始化为一个大值(表示不可达) dp[0] = 0; // base case:金额为0,需要0个硬币 // 动态规划求解 for (int i = 1; i <= amount; i++) { for (int coin : coins) { if (i - coin >= 0) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } } // 如果 dp[amount] 仍为 amount + 1,说明无法组成该金额 return dp[amount] > amount ? -1 : dp[amount]; } } ``` --- ## 🔍 举例说明: 假设 `coins = [1, 2, 5]`, `amount = 11` - 最少硬币组合是:5 + 5 + 1 = 11,共 3 枚硬币 - 输出应为:`3` --- ## 🧠 动态规划思路: - `dp[i]` 表示组成金额 `i` 所需的最小硬币数 - 状态转移方程: ``` dp[i] = min(dp[i], dp[i - coin] + 1) ``` 对于每个 `coin`,如果 `i - coin >= 0`,就尝试用这个硬币来凑 `i`,并更新 `dp[i]` --- ## ⚙️ 时间复杂度分析: - 时间复杂度:`O(amount * coins.length)`,其中 amount 是目标金额,coins 是硬币种类数 - 空间复杂度:`O(amount)`,用于存储 dp 数组 --- ## ✅ 示例运行: ```java public class Main { public static void main(String[] args) { Solution solution = new Solution(); int[] coins = {1, 2, 5}; int amount = 11; System.out.println(solution.coinChange(coins, amount)); // 输出:3 } } ``` --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值