如何在硬币找零问题中添加有限的硬币? (自下而上 - 动态规划)
我是动态编程的新手(和C++,但我有更多的经验,有些事情我仍然不知道)。如何将有限硬币添加到硬币找零问题(请参阅下面的代码 - 有点混乱,但我仍在研究它)。我有一个变量 nr[100] 来记录硬币的数量(还在我的 read_values() 中创建了一些条件)。我不知道在我的代码中哪里可以使用它。
该代码认为我们有无限的硬币供应(我不希望这样)。 它是用自下而上的方法(动态规划)制定的。
我的代码受到以下视频的启发:Youtube
#include <iostream>
using namespace std;
int C[100], b[100], n, S, s[100], nr[100], i, condition=0, ok=1;
void read_values() //reads input
{
cin >> n; // coin types
cin >> S; // amount to change
for (i=1; i<=n; i++)
{
cin >> b[i]; //coin value
cin>>nr[i]; //coin amount
if(nr[i]==0)b[i]=0; //if there are no coin amount then the coin is ignored
condition+=b[i]*nr[i]; //tests to see if we have enough coins / amount of coins to create a solution
if(b[i]>S)
{
b[i]=0;
}
}
if(S>condition)
{
cout<<endl;
cout<<"Impossible!";
ok=0;
}
}
void payS()
{
int i, j;
C[0] = 0; // if amount to change is 0 then the solution is 0
for (j=1; j<=S; j++)
{
C[j] = S+1;
for (i=1; i<=n; i++)
{
if (b[i] <= j && 1 + C[j - b[i]] < C[j])
{
C[j] = 1 + C[j - b[i]];
s[j] = b[i];
}
}
}
cout << "Minimum ways to pay the amount: " << C[S] << endl;
}
void solution(int j)
{
if (j > 0)
{
solution(j - s[j]);
cout << s[j] << " ";
}
}
int main()
{
read_values();
if(ok!=0)
{
payS();
cout << "The coins that have been used are: ";
solution(S);
}
}
I am new to dynamic programming (and C++ but I have more experience, some things are still unknown to me). How can I add LIMITED COINS to the coin change problem (see my code below - is a bit messy but I'm still working on it). I have a variable nr[100] that registers the number of coins (also created some conditions in my read_values() ). I don't know where can I use it in my code.
The code considers that we have an INFINITE supply of coins (which I don't want that).
It is made in the bottom-up method (dynamic programming).
My code is inspired from this video: Youtube
#include <iostream>
using namespace std;
int C[100], b[100], n, S, s[100], nr[100], i, condition=0, ok=1;
void read_values() //reads input
{
cin >> n; // coin types
cin >> S; // amount to change
for (i=1; i<=n; i++)
{
cin >> b[i]; //coin value
cin>>nr[i]; //coin amount
if(nr[i]==0)b[i]=0; //if there are no coin amount then the coin is ignored
condition+=b[i]*nr[i]; //tests to see if we have enough coins / amount of coins to create a solution
if(b[i]>S)
{
b[i]=0;
}
}
if(S>condition)
{
cout<<endl;
cout<<"Impossible!";
ok=0;
}
}
void payS()
{
int i, j;
C[0] = 0; // if amount to change is 0 then the solution is 0
for (j=1; j<=S; j++)
{
C[j] = S+1;
for (i=1; i<=n; i++)
{
if (b[i] <= j && 1 + C[j - b[i]] < C[j])
{
C[j] = 1 + C[j - b[i]];
s[j] = b[i];
}
}
}
cout << "Minimum ways to pay the amount: " << C[S] << endl;
}
void solution(int j)
{
if (j > 0)
{
solution(j - s[j]);
cout << s[j] << " ";
}
}
int main()
{
read_values();
if(ok!=0)
{
payS();
cout << "The coins that have been used are: ";
solution(S);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我假设您需要使用
nbr
表生成正整数值amount
的找零,其中nbr[n]
是价值n
的可用硬币数量。我还假设nbr[0]
实际上毫无意义,因为它只代表没有价值的硬币。大多数动态规划问题通常都是在选择选项 A 与选项 B 的二元决策上递归。通常,一个选项是“选择这个”,另一个选项是“不选择这个并使用可用集合的其余部分”。这个问题确实没有什么不同。
首先,我们来解决没有缓存的递归动态问题。
我将用名为
“cointable”
的数据结构替换您的nbr
变量。这用于跟踪可用的硬币集和为任何给定解决方案路径选择的硬币集:cointable::table
实际上与您的nbr
数组。coinbase::number
是表中值的总和。它不用于跟踪可用硬币,但用于跟踪更好的解决方案。现在我们可以介绍没有查找缓存的递归解决方案。
递归的每个步骤都会执行以下操作:
在可用硬币集合中查找不大于要解决的目标金额的最高价值的硬币
递归选项 A:选择从步骤 1 中选择的该硬币。现在使用以下方法(递归地)解决减少的金额减少了可用硬币的数量。
递归选项 B:不要选择这枚硬币,而是递归使用比步骤 1 中找到的价值更低的第一个硬币。
比较 2 和 3 的递归结果。选择硬币数量较少的硬币使用过
这是代码 - 不使用最佳查找缓存
然后快速演示如何计算 128 美分的零钱,给定较大硬币数量有限教派:{1:100, 5:20, 10:10, 25:1, 50:1}
这应该有效。对于大多数人来说,对于一美元以下的零钱来说,它可能已经足够快了,因此不需要缓存。所以我们有可能就在这里停下来并完成。
我要在这里停下来
我开始研究一种引入“缓存”以避免冗余递归的解决方案。但在对其进行基准测试并研究算法如何快速找到最佳解决方案之后,我不太确定缓存是否必要。我最初尝试为可解和不可解的解决方案插入缓存表,只是使代码变慢。我需要研究如何让它发挥作用——如果有必要的话。
I'm working under the assumption that you need to generate change for a positive integer value,
amount
using yournbr
table wherenbr[n]
is the number of coins available of valuen
. I'm also working under the assumption thatnbr[0]
is effectively meaningless since it would only represent coins of no value.Most dynamic programming problems are typically recursing on a binary decision of choosing option A vs option B. Often times one option is "pick this one" and other is "don't pick this one and use the rest of the available set". This problem is really no different.
First, let's solve the recursive dynamic problem without a cache.
I'm going to replace your
nbr
variable with a data structure called a"cointable"
. This is used to keep track of both the available set of coins and the set of coins selected for any given solution path:cointable::table
is effectively the same thing as yournbr
array.coinbase::number
is the summation of the values in table. It's not used to keep track of available coins, but it is used to keep track of the better solution.Now we can introduce the recursive solution without a lookup cache.
Each step of the recursion does this:
Look for the highest valuable coin that is in the set of available coins not greater than the target amount being solved for
Recurse on option A: Pick this coin selected from step 1. Now solve (recursively) for the reduced amount using the reduced set of available coins.
Recurse on option B: Don't pick this coin, but instead recurse with the first coin of lesser value than what was found in step 1.
Compare the recursion results of 2 and 3. Pick the one with lesser number of coins used
Here's the code - without using an optimal lookup cache
And then a quick demonstration for how to calculate change for 128 cents given a limited amount of coins in the larger denominations: {1:100, 5:20, 10:10, 25:1, 50:1}
And that should work. And it might be fast enough for most making change for anything under a dollar such that a cache is not warranted. So it's possible we can stop right here and be done.
And I am going to stop right here
I started to work on a solution that introduces a "cache" to avoid redundant recursions. But after benchmarking it and studying how the algorithm finds the best solution quickly, I'm not so sure a cache is warranted. My initial attempt to insert a cache table for both solvable and unsolvable solutions just made the code slower. I'll need to study how to make it work - if it's even warranted at all.
也许您希望我们修复您的代码,但我实现了我自己的解决方案版本。希望我自己的版本对你有用,至少在教育上。
当然,我使用了动态编程方法。
我保留了可能进行更改的向量。接下来的每个金额都是由之前的金额加上几个相同价值的硬币组成的。
使用过的硬币的历史也被保留,这使我们能够将每次更改恢复为精确给定硬币的组合。
在代码之后,您可以看到控制台输出,其中显示了用硬币 2x4、3x3、5x2、10x1 组成找零 13 的示例(这里第二个数字是硬币数量)。
输入硬币及其数量在 main() 函数开始处的
coins
向量内给出,您可以用您想要的任何内容填充此向量,例如通过控制台用户输入。需要表示的更改在变量change
内给出。不要忘记在代码和控制台输出之后查看 Post Scriptum (PS.),它有一些有关算法的更多详细信息。
完整代码如下:
在线尝试!
输出:
PS. 如您所愿注意到,算法的主要动态编程部分非常小,只有以下几行:
该部分保留所有当前可组合的总和(更改)。算法从 0 的货币变化开始,然后将 1×1 硬币增量添加到所有可能的当前变化(总和)中,从而形成新的总和(包括这个新硬币)。
每个总和都保留了一个计数器,其中包含所有可能的组成方式,并且它还跟踪导致该总和的所有最后硬币。最后一组硬币允许进行回溯,以恢复硬币的具体组合,而不仅仅是计算该总和的方法的数量。
Maybe you wanted us to fix your code, but instead I implemented my own version of solution. Hopefully my own version will be useful somehow for you, at least educationally.
Of course I used Dynamic Programming approach for that.
I keep a vector of possible to compose changes. Each next sums is composed of previous sums by adding several coins of same value.
History of used coins is also kept, this allows us to restore each change as combination of exactly given coins.
After code you can see console output that shows example of composing change 13 out of coins 2x4, 3x3, 5x2, 10x1 (here second number is amount of coins).
Input coins and their amount is given inside
coins
vector at start of main() function, you can fill this vector with anything you want, for example by taking console user input. Needed to be represented change is given inside variablechange
.Don't forget to see Post Scriptum (PS.) after code and console output, it has some more details about algorithm.
Full code below:
Try it online!
Output:
PS. As you may have noticed, main Dynamic Programming part of algorithm is very tiny, just following lines:
This part keeps all currently composable sums (changes). Algo starts from money change of 0, then incrementally adds 1-by-1 coin to all possible current changes (sums), thus forming new sums (including this new coin).
Each sum keeps a counter of all possible ways to compose it plus it keeps track of all last coins that lead to this sum. This last coins set allows to do back-tracking in order to restore concrete combinations of coins, not just amount of ways to compute this sum.