动态规划变更问题(限量硬币)。 我正在尝试创建一个以 INPUT:
为目标的程序 int coinValues[]; //e.g [coin1,coin2,coin3]
int coinLimit[]; //e.g [2 coin1 available,1 coin2 available,...]
int amount; //the amount we want change for.
输出:
int DynProg[]; //of size amount+1.
输出应该是一个大小金额+ 1 的数组,其中每个单元格代表我们需要更改单元格索引量的最佳硬币数量。
示例:假设我们在索引处有数组Array:5,内容为2。 这意味着,为了更改5的金额( INDEX ),您需要2个(单元格内容)硬币(最佳解决方案)。
基本上我需要这个视频的第一个数组的输出(C [p]) 。这与有限硬币的 DIFFERENCE 完全相同。 Link to Video.
注意:看视频要理解,忽略视频的第二个数组,并记住我不需要组合,但DP阵列,所以我可以找到哪些硬币作为更改。
谢谢。
答案 0 :(得分:1)
考虑下一个伪代码:
for every coin nominal v = coinValues[i]:
loop coinLimit[i] times:
starting with k=0 entry, check for non-zero C[k]:
if C[k]+1 < C[k+v] then
replace C[k+v] with C[k]+1 and set S[k+v]=v
是否清楚?
答案 1 :(得分:0)
这就是您要寻找的。 进行的假设:硬币值按降序排列
public class CoinChangeLimitedCoins {
public static void main(String[] args) {
int[] coins = { 5, 3, 2, 1 };
int[] counts = { 2, 1, 2, 1 };
int target = 9;
int[] nums = combine(coins, counts);
System.out.println(minCount(nums, target, 0, 0, 0));
}
private static int minCount(int[] nums, int target, int sum, int current, int count){
if(current > nums.length) return -1;
if(sum == target) return count;
if(sum + nums[current] <= target){
return minCount(nums, target, sum+nums[current], current+1, count+1);
} else {
return minCount(nums, target, sum, current+1, count);
}
}
private static int[] combine(int[] coins, int[] counts) {
int sum = 0;
for (int count : counts) {
sum += count;
}
int[] returnArray = new int[sum];
int returnArrayIndex = 0;
for (int i = 0; i < coins.length; i++) {
int count = counts[i];
while (count != 0) {
returnArray[returnArrayIndex] = coins[i];
returnArrayIndex++;
count--;
}
}
return returnArray;
}
}
答案 2 :(得分:0)
O(nk)
来自我前一段时间写的社论中的解决方案:
我们从在O(k*sum(c))
中运行的基本DP解决方案开始。我们有dp
数组,其中dp[i][j]
存储的总和为i
的第一个j
面额的硬币数量最少。我们进行以下转换:dp[i][j] = min(dp[i - 1][j - cnt * value[i]] + cnt) for cnt from 0 to j / value[i]
。
要将其优化为O(nk)
解决方案,我们可以使用双端队列存储上一次迭代的最小值并进行过渡O(1)
。基本思想是,如果我们要在某个数组中找到最后m
个值中的最小值,则可以维持一个不断增加的双端队列,该队列存储该最小值的可能候选对象。在每一步中,我们都会在双端队列结束时弹出大于当前值的值,然后再将当前值推入后双端队列。由于当前值既在右边,又小于我们弹出的值,因此我们可以确保它们永远不会是最小值。然后,如果双端队列中的第一个元素超过m
个元素,则将其弹出。现在,每个步骤的最小值只是双端队列中的第一个元素。
我们可以对这个问题应用类似的优化技巧。对于每种硬币类型i
,我们将按以下顺序计算dp
数组的元素:对于j % value[i]
的每个可能值,按递增顺序,我们处理j
的值当除以value[i]
时,该余数按递增顺序产生。现在,我们可以应用双端队列优化技巧,以在恒定时间内找到min(dp[i - 1][j - cnt * value[i]] + cnt) for cnt from 0 to j / value[i]
。
伪代码:
let n = number of coin denominations
let k = amount of change needed
let v[i] = value of the ith denomination, 1 indexed
let c[i] = maximum number of coins of the ith denomination, 1 indexed
let dp[i][j] = the fewest number of coins needed to sum to j using the first i coin denominations
for i from 1 to k:
dp[0][i] = INF
for i from 1 to n:
for rem from 0 to v[i] - 1:
let d = empty double-ended-queue
for j from 0 to (k - rem) / v[i]:
let currval = rem + v[i] * j
if dp[i - 1][currval] is not INF:
while d is not empty and dp[i - 1][d.back() * v[i] + rem] + j - d.back() >= dp[i - 1][currval]:
d.pop_back()
d.push_back(j)
if d is not empty and j - d.front() > c[i]:
d.pop_front()
if d is empty:
dp[i][currval] = INF
else:
dp[i][currval] = dp[i - 1][d.front() * v[i] + rem] + j - d.front()
答案 3 :(得分:0)
您可以检查这个问题:Minimum coin change problem with limited amount of coins。 顺便说一句,我根据上面链接的算法创建了 C++ 程序:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <limits>
using namespace std;
void copyVec(vector<int> from, vector<int> &to){
for(vector<int>::size_type i = 0; i < from.size(); i++)
to[i] = from[i];
}
vector<int> makeChangeWithLimited(int amount, vector<int> coins, vector<int> limits)
{
vector<int> change;
vector<vector<int>> coinsUsed( amount + 1 , vector<int>(coins.size()));
vector<int> minCoins(amount+1,numeric_limits<int>::max() - 1);
minCoins[0] = 0;
vector<int> limitsCopy(limits.size());
copy(limits.begin(), limits.end(), limitsCopy.begin());
for (vector<int>::size_type i = 0; i < coins.size(); ++i)
{
while (limitsCopy[i] > 0)
{
for (int j = amount; j >= 0; --j)
{
int currAmount = j + coins[i];
if (currAmount <= amount)
{
if (minCoins[currAmount] > minCoins[j] + 1)
{
minCoins[currAmount] = minCoins[j] + 1;
copyVec(coinsUsed[j], coinsUsed[currAmount]);
coinsUsed[currAmount][i] += 1;
}
}
}
limitsCopy[i] -= 1;
}
}
if (minCoins[amount] == numeric_limits<int>::max() - 1)
{
return change;
}
copy(coinsUsed[amount].begin(),coinsUsed[amount].end(), back_inserter(change) );
return change;
}
int main()
{
vector<int> coins;
coins.push_back(20);
coins.push_back(50);
coins.push_back(100);
coins.push_back(200);
vector<int> limits;
limits.push_back(100);
limits.push_back(100);
limits.push_back(50);
limits.push_back(20);
int amount = 0;
cin >> amount;
while(amount){
vector<int> change = makeChangeWithLimited(amount,coins,limits);
for(vector<int>::size_type i = 0; i < change.size(); i++){
cout << change[i] << "x" << coins[i] << endl;
}
if(change.empty()){
cout << "IMPOSSIBE\n";
}
cin >> amount;
}
system("pause");
return 0;
}
答案 4 :(得分:0)
c#代码
private static int MinCoinsChangeWithLimitedCoins(int[] coins, int[] counts, int sum)
{
var dp = new int[sum + 1];
Array.Fill(dp, int.MaxValue);
dp[0] = 0;
for (int i = 0; i < coins.Length; i++) // n
{
int coin = coins[i];
for (int j = 0; j < counts[i]; j++) //
{
for (int s = sum; s >= coin ; s--) // sum
{
int remainder = s - coin;
if (remainder >= 0 && dp[remainder] != int.MaxValue)
{
dp[s] = Math.Min(1 + dp[remainder], dp[s]);
}
}
}
}
return dp[sum] == int.MaxValue ? -1 : dp[sum];
}