给定值N
,如果我们要为N cents
进行找零,并且有无限数量的S = { S1, S2, .. , Sm}
面值的硬币供应,那么进行找零的最佳方法是N cents
。
示例:
S = {2, 5, 10}
N = 6, then optimal solution is : 2, 2, 2
我有以下工作代码:
public static void main(String argv[]) {
long n = 10L;
int[] combo = new int[100];
int[] amounts = { 2, 5, 10 };
ways(n, amounts, combo, 0, 0, 0);
}
public static void ways(long n, int[] amounts, int[] combo, int startIndex, int sum, int index) {
if (sum == n) {
printArray(combo, index);
}
if (sum > n) {
return;
}
for (int i = 0; i < amounts.length; i++) {
sum = sum + amounts[i];
combo[index] = amounts[i];
ways(n, amounts, combo, startIndex, sum, index + 1);
sum = sum - amounts[i];
}
}
public static void printArray(int[] combo, int index) {
for (int i = 0; i < index; i++) {
System.out.print(combo[i] + " ");
}
System.out.println();
}
输出:
2 2 2 2 2
5 5
10
在这里,我只需要使用较少数量的硬币来实现最佳组合,因此在此示例代码中只有10
。
但是此代码使用递归方法,我对N的值是Long
类型,所以当N的值增加时,我会得到stackoverflow
错误。
我在这里采用的递归方法是不正确的,解决此问题的正确方法是什么?
更新:
基于MBo答案,我尝试了以下程序,但无法获得正确的结果:
static void testcase() {
// make int array A of size N+1
int N = 6;
int[] A = new int[N + 1];
// make int array P of size N+1
int[] P = new int[N + 1];
// fill A[] with large value (len(S) + 1)
int[] S = { 2, 5, 10 };
int lengthOfS = S.length;
for (int i = 0; i < A.length; i++) {
A[i] = lengthOfS + 1;
}
A[0] = 0;
for (int s : S) {// coin value
for (int i = s; i <= N; i++) {
if (A[i - s] < A[i] + 1) { // using this coin we can get better
// result for sum i
A[i] = A[i - s] + 1;
P[i] = s; // write down coin for this sum
}
}
}
System.out.println(Arrays.toString(P)); // [0, 0, 2, 2, 2, 5, 2]
System.out.println(A[N]);// 3
int idx = N;
for (int i = 0; i < A[N]; i++) {
int result = idx - P[idx];
System.out.println(result); // 4 2 0
idx = result;
}
}
此代码显示:
[0, 0, 2, 2, 2, 5, 2]
3
4
2
0
如何修复此代码?
答案 0 :(得分:3)
对于固定集S = {2, 5, 10}
,解决方案非常简单:
N=1,3
的解决方案
如果N为奇数,则必须使用5-所以N=N-5
现在使用贪婪方法:获取尽可能多的10-s,然后获取尽可能多的2-s
def best(N):
print(N, end = ": ")
if (N % 2):
print("5", end = " ")
N = N - 5
if N >= 10:
print("10*", N//10, end = " ")
N = N % 10
if N > 1:
print("2*", N//2, end = " ")
21: 5 10* 1 2* 3
22: 10* 2 2* 1
23: 5 10* 1 2* 4
24: 10* 2 2* 2
通常,您可以使用动态编程找到最佳解决方案。
第一种方法是“记忆化”-您必须通过选择最佳解决方案来实现递归方法,然后将存储的中间结果添加到hashmap或其他结构中。简单的实现:
S = [2, 3, 5, 7, 11]
dic = {}
def rec(summ):
if summ == 0:
return 0
rd = dic.get(summ)
if rd != None:
return rd
minn = 9999999999999
for s in S:
if s <= summ:
minn = min(minn, 1 + rec(summ - s))
dic[summ] = minn
return minn
N = 1000
print(rec(N))
>>92
另一种方法是使用表格-使用第一项填充最佳结果,然后使用第二项更新解决方案,依此类推。
伪代码
make int array A of size N+1
make int array P of size N+1
fill A[] with large value (MaxInt` or at least `N/min(S))
A[0] = 0
for s in S: //coin value
for (i = s; i <= N; i++)
if A[i - s] < A[i] + 1 //using this coin we can get better result for sum i
A[i] = A[i - s] + 1
P[i] = s //write down coin for this sum
现在,我们拥有数量最多的A[N]
,并且可以使用P[N], P[N - P[N]]...
序列检索所需的硬币。
有效的Python代码
S = [2, 3, 5, 7, 11]
N = 17
A = [0] + [10000] * N
P = [0] * (N + 1)
for s in S: #coin value
for i in range(s, N + 1):
if A[i - s] < A[i] + 1: #using this coin we can get better result for sum i
A[i] = A[i - s] + 1
P[i] = s #write down coin for this sum
print(A) #for reference
i = N
while i > 0:
print(P[i], end = " ")
i = i - P[i]
>> [0, 10000, 1, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 3, 2, 3]
>> 11 3 3
注意-如果每个硬币只能使用一次,则必须向后进行内循环,以避免多次添加相同的硬币
答案 1 :(得分:1)
由于硬币数量少,并且由于数量可能很大,因此回溯可能会提供一个好的解决方案
这是C ++的实现
(注意:此代码已发布,但我找不到该帖子。问题已删除吗?)
首先对硬币进行降序排序,以加快搜索速度。
为了最大程度地减少硬币数量,我们首先尝试使用最大可能数量的最大硬币数量的解决方案。
在给定的搜索中,如果当前的硬币数量大于当前的最小硬币数量,我们将停止搜索(“过早放弃”)。
在代码中,“ UP”表示我们将考虑添加价值较低的硬币
“下降”表示我们将尝试减少高价值硬币的数量。
在给定的步骤中,我们维护一个对应于每个硬币值的硬币数量的数组
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
// The order of array coins is modified
std::vector<int> get_change(std::vector<int>& coins, int amount) {
std::vector<int> n_coins(coins.size(), 0);
std::vector<int> n_coins_opt(coins.size(), 0);
int n = coins.size();
std::sort(coins.begin(), coins.end(), std::greater<int>());
int sum = 0; // current sum
int i = 0; // index of the coin being examined
int n_min_coins = amount / coins[n - 1] + 1;
int n_total_coins = 0;
bool up_down = true;
while (true) { // UP
if (up_down) {
n_coins[i] = (amount - sum) / coins[i]; // max possible number of coins[i]
sum += n_coins[i] * coins[i];
n_total_coins += n_coins[i];
if (sum == amount) {
if (n_total_coins < n_min_coins) {
n_min_coins = n_total_coins;
n_coins_opt = n_coins;
}
up_down = false;
sum -= n_coins[i] * coins[i];
n_total_coins -= n_coins[i];
n_coins[i] = 0;
i--;
}
else {
if (i == (n - 1) || (n_total_coins >= n_min_coins)) { // premature abandon
sum -= n_coins[i] * coins[i];
n_total_coins -= n_coins[i];
n_coins[i] = 0;
up_down = false;
i--;
}
else {
i++;
}
}
}
else { // DOWN
if (i < 0) break;
if (n_coins[i] == 0) {
if (i == 0) break;
i--;
}
else {
sum -= coins[i];
n_coins[i] --;
n_total_coins--;
i++;
up_down = true;
}
}
}
return n_coins_opt;
}
int main() {
std::vector<int> coins = {2, 5, 10};
int amount = 1731;
auto n_coins = get_change(coins, amount);
int sum = std::accumulate (n_coins.begin(), n_coins.end(), 0);
if (sum == 0) {
std::cout << "no solution\n";
} else {
std::cout << amount << " = ";
for (int i = 0; i < n_coins.size(); i++) {
std::cout << n_coins[i] << "*" << coins[i] << " ";
}
std::cout << "\n";
}
return 1;
}