硬币交换变体的动态编程

Posted

技术标签:

【中文标题】硬币交换变体的动态编程【英文标题】:Dynamic Programming for a variant of the coin exchange 【发布时间】:2018-01-24 12:34:57 【问题描述】:

我有兴趣解决硬币交换问题的变体。回想一下硬币交换问题的正式定义:

给定一个值N,如果我们想换N美分,我们每个S = S1, S2, .. , Sm 整数值硬币,我们可以通过多少种方式进行更改?硬币的顺序无关紧要。例如,对于 N = 4 和 S = 1,2,3,有四种解决方案:1,1,1,1,1, 1,2,2,2,1,3。所以输出应该是 4。对于 N = 10 和 S = 2, 5, 3, 6,有五种解决方案:2,2,2,2 ,2、2,2,3,3、2,2,6、2,3,5 和 5,5。所以输出应该是 5。See here 了解更多详情。

Here,可以为这个问题找到一种基于制表的 DP 解决方案。该解决方案基于以下递归关系:

count(S, m, n) = count(S, m - 1, n) + count(S, m, n - S[m - 1]);

基本情况:

count(S, m, n < 0) = 0
count(S, m, n = 0) = 1
count(S, m <= 0, n >= 1) = 0

直观地说,这种递归关系将问题定义为两个子问题的解决方案:我们丢弃硬币的那些,以及我们假设硬币一次逐步使用一次的那些。

问题:我如何修改这个递归关系来计算我们可以总结为 N 的方式的数量,而不管顺序和被加数的数量是偶数吗?例如,对于 N = 4 和 S = 1,2,3,总共有四个解(不考虑顺序):1,1,1,1 ,1,1,2,2,2,1,3,但其中只有 3 个具有偶数个和数,即 1,1,1,1,2,2 ,1,3。

以前的研究:起初我以为我可以在丢弃硬币的情况下将要求的金额增加一倍,并在我们将使用一些硬币的情况下要求每枚硬币被消耗两次,即:

count(S, m, n) = count(S, m - 1, 2*n) + count(S, m, n - 2*S[m - 1]);

这适用于某些示例案例,但它不起作用。有什么提示吗?

【问题讨论】:

我猜您可以使用两个单独的函数来使用奇数和偶数:o(S,m,n)= o(m-2, n)+e(m, n-s[m-1])e(S,m,n)= e(m-2, n)+o(m, n-s[m-1]),基数为:o(S, m, n &lt; 0) = 0 o(S, m, n = 0) = 0 o(S, m &lt;= 0, n &gt;= 1) = 0e(S, m, n &lt; 0) = 0 e(S, m, n = 0) = 1 e(S, m &lt;= 0, n &gt;= 1) = 0 【参考方案1】:

您需要在递归中添加一个标志,该标志是迄今为止使用的和数的奇偶校验。

使用 summand 时,您会翻转标志 (S(n-v[m],m,!parity))。

基本情况是:

n=0 parity=0 -> 1

n=0 parity=1 -> 0

n<0 or m<0 (array is 0-indexed) -> 0

下面是c++中的递归函数。当然,您需要记住它,以便它能够处理更大的值。

int S(int n,int m,bool parity)

    if (n==0)
    
        if (parity==1)
            return 0;
        else 
            return 1;
    
    if (m<0 || n<0)
        return 0;
    return S(n,m-1,parity)+S(n-v[m],m,!parity);

【讨论】:

以上是关于硬币交换变体的动态编程的主要内容,如果未能解决你的问题,请参考以下文章

硬币变化(动态编程)

动态编程,硬币变化,内存泄漏?

动态硬币变化算法(最佳结果)

每日编程-20170327

算法-硬币排成线II(动态规划)

硬币找零问题之动态规划