找到产生给定值的最小硬币集

Posted

技术标签:

【中文标题】找到产生给定值的最小硬币集【英文标题】:Finding the minimum set of coins that make a given value 【发布时间】:2021-12-25 07:10:32 【问题描述】:

我一直在试图弄清楚是否有办法获得用于找零的最佳最小硬币集。

贪心算法有一个问题,例如如果我们有一组硬币 1, 5, 6, 9,我们想要得到值 11。贪心算法会给我们 9,1, 1 但最佳解决方案是 5, 6

通过阅读site,我发现这种方法可以为我们提供所需的最少硬币总数。有没有办法从中得到一套硬币?

【问题讨论】:

更新动态规划时,只需保留一些数组last_coin[],其中last_coin[i] 等于最后添加的任何一个硬币,以便获得最佳硬币子集的总和到i。然后,可以通过从i 开始并追溯至i - last_coin[i] 直到达到 0 来找到总和为一个数字的实际硬币集合。 @Telescope 如果您不介意可以通过代码向我展示我们将如何做到这一点吗? 【参考方案1】:

我假设您已经知道动态编程方法可以只找到所需的最小数量硬币。假设您想找到创造总价值K 的最小硬币数量。然后,您的代码可能是

vector <int> min_coins(K + 1);
min_coins[0] = 0; // base case
for(int k = 1; k <= K; ++k) 
    min_coins[k] = INF;
    for(int c : coins)  // coins[] contains all values of coins
        if(k - c >= 0) 
            min_coins[k] = min(min_coins[k], min_coins[k - c] + 1);
        
    

回答您的问题:为了找到实际集合中最小的硬币,我们可以简单地保留另一个数组last_coin[],其中last_coin[k] 等于之前的硬币最后添加到最佳硬币集合中,总和为k。为了说明这一点:

vector <int> min_coins(K + 1), last_coin(K + 1);
min_coins[0] = 0; // base case
for(int k = 1; k <= K; ++k) 
    min_coins[k] = INF;
    for(int c : coins) 
        if(k - c >= 0) 
            if(min_coins[k - c] + 1 < min_coins[k]) 
                min_coins[k] = min_coins[k - c] + 1;
                last_coin[k] = c; // !!!
            
        
    

这如何让你找到这组硬币?假设我们想找到总和为K 的最佳硬币集。然后,我们知道last_coin[K] 持有集合中的一个硬币,因此我们可以将last_coin[K] 添加到集合中。之后,我们从K 中减去last_coin[K] 并重复直到K = 0。显然,这将构建一个(不一定)最小大小的硬币集合,总和为 K。

可能的实现:

int value_left = K;
while(value_left > 0) 
    last_coin[value_left] is added to the set
    value_left -= last_coin[value_left]

【讨论】:

以上是关于找到产生给定值的最小硬币集的主要内容,如果未能解决你的问题,请参考以下文章

如何使用递归在最小零钱问题中输出实际的硬币组合

给定一些美元价值时如何找到所有硬币组合[关闭]

动态规划算法:硬币的最大总和(小于或等于k)

给定硬币的所有可能总和

硬币变化(动态编程)

查找所有排列以获得给定的总和(硬币找零问题)