改变硬币实施问题

时间:2013-07-05 04:06:29

标签: algorithm dynamic-programming

尝试使用DP解决这个经典问题时遇到了实施问题。 问题是给出一组硬币,并返回进行更改的方式。

DP等式如下:     DP [i] + = DP [i - coin [j]]
其中DP [i]表示为i进行更改的方式的数量。

这是一个简单的实现,这是不正确的:

int make_change_wrong(int coin[], int size, int change) {
    vector<int> DP(change + 1, 0);
    DP[0] = 1;

    for (int i = 1; i <= change; ++i) {
        for (int j = 0; j < size; ++j) {
            if (i - coin[j] >= 0 ) {
                DP[i] += DP[i - coin[j]];
            }
        }
    }

    return DP[change];
}

给定输入: int coin [] = {1,5} 改变= 6。

make_change_wrong(coin,2,6)返回3,但2是正确的。

使用相同的逻辑,我以不太直观的方式重写它并得到正确的答案:

int make_change(int coin[], int size, int change) {
    vector<int> DP(change + 1, 0);
    DP[0] = 1;

    for (int i = 0; i < size; ++i) {
        for (int j = coin[i]; j <= change; ++j) {
            DP[j] += DP[j - coin[i]];
        }
    }

    return DP[change];
}

这让我很困惑,因为对我来说,他们是一样的...... 有人能说明两个实现中的问题吗?

3 个答案:

答案 0 :(得分:4)

你的第一个算法错了。

DP [5] = 2 {1,1,1,1,1},{5}

DP [6] = DP [5] + DP [1] = 3

你正在计算{5,1}两次。 的 EDITED 因此,执行此操作的标准技巧是您保留允许使用的面额计数

DP[i,m] = DP[i-coin[m],m] + DP[i,m-1]

表示使用范围[1..m]内的硬币更改i金额的方式的数量。 显然,你要么使用mth面额,要么不使用。

你正在使用的第二个算法是做同样的技巧,但这是一个非常聪明的方法,拿第i个硬币,看看你可以使用它生成的所有变化。这将避免过度计数,因为你避免做{1,5}和{5,1}之类的事情。

答案 1 :(得分:0)

这个问题出现在面试准备书 Cracking the Coding Interview 中,并且书中给出的解决方案根本没有优化。它使用递归(没有DP)重复计算子问题,因此在O(N ^ 3)中运行,这是特别具有讽刺意味的,因为它是动态编程章节的一部分。

这是一个非常简单的工作解决方案(Java),它使用DP并在O(N)时间内运行。

static int numCombos(int n) {
    int[] dyn = new int[n + 1];
    Arrays.fill(dyn, 0);
    dyn[0] = 1;
    for (int i = 1;  i <= n; i++) dyn[i] += dyn[i - 1];
    for (int i = 5;  i <= n; i++) dyn[i] += dyn[i - 5];
    for (int i = 10; i <= n; i++) dyn[i] += dyn[i - 10];
    for (int i = 25; i <= n; i++) dyn[i] += dyn[i - 25];
    return dyn[n];
}

答案 2 :(得分:0)

请尝试输入您的第二种方法:

coin[5] = {1,5,10,20,30};
make_change(coin,5,30);

它返回21.请检查我的测试用例。