硬币改变复发解决方案

时间:2015-06-17 06:35:29

标签: dynamic-programming recurrence

给定值N,如果我们想要改变N美分,并且我们每个S = {S1,S2,..,Sm}值的硬币都有无限供应,我们可以通过多少方式进行更改?硬币的顺序并不重要。但是还有一个额外的限制:你只能用K币进行更改。

例如,对于N = 4,k = 2且S = {1,2,3},有两个解:{2,2},{1,3}。所以输出应该是2。

解决方案:

int getways(int coins, int target, int total_coins, int *denomination, int size, int idx)
    {
            int sum = 0, i;
            if (coins > target || total_coins < 0)
                    return 0;
            if (target == coins && total_coins == 0)
                    return 1;
            if (target == coins && total_coins < 0)
                    return 0;
            for (i=idx;i<size;i++) {
                    sum += getways(coins+denomination[i], target, total_coins-1, denomination, size, i);
            }
            return sum;
    }

    int main()
    {
            int target = 49;
            int total_coins = 15;
            int denomination[] = {1, 2, 3, 4, 5};
            int size = sizeof(denomination)/sizeof(denomination[0]);
            printf("%d\n", getways(0, target, total_coins, denomination, size, 0));
    }

以上是递归解决方案。但是我需要有关动态编程解决方案的帮助:

dp[i][j][k]代表ij元素和k个硬币的总和。

所以,

dp[i][j][k] = dp[i][j-1][k] + dp[i-a[j]][j][k-1]

我的复发关系是否正确?

1 个答案:

答案 0 :(得分:1)

我真的不明白你的复发关系:

  

dp[i][j][k]代表ij元素和k个硬币的总和。

我认为你走在正确的轨道上,但我建议简单地删除中间维度[j],然后使用dp[sum][coinsLeft],如下所示:

dp[0][0] = 1  // coins: 0, desired sum: 0  =>  1 solution
dp[i][0] = 0  // coins: 0, desired sum: i  =>  0 solutions

dp[sum][coinsLeft] = dp[sum - S1][coinsLeft-1]
                   + dp[sum - S2][coinsLeft-1]
                   + ...
                   + dp[sum - SM][coinsLeft-1]

然后在dp[N][K]找到答案(=添加 K 硬币以获得 N 分钱的方式的数量)

这里有一些示例代码(我建议你不要看,直到你自己试图解决它。这是一个很好的练习):

  

public static int combinations(int numCoinsToUse, int targetSum, int[] denom) {
    // dp[numCoins][sum]  ==  ways to get sum using numCoins
    int[][] dp = new int[numCoinsToUse+1][targetSum];

     // Any sum (except 0) is impossible with 0 coins
     for (int sum = 0; sum < targetSum; sum++) {
         dp[0][sum] = sum == 0 ? 1 : 0;
     }

     // Gradually increase number of coins
     for (int c = 1; c <= numCoinsToUse; c++)
         for (int sum = 0; sum < targetSum; sum++)
             for (int d : denom)
                 if (sum >= d)
                     dp[c][sum] += dp[c-1][sum - d];
     return dp[numCoinsToUse][targetSum-1];
 }

使用您的示例输入:

combinations(2, 4, new int[] {1, 2, 3} ) // gives 2