硬币交换变体的动态编程
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 < 0) = 0 o(S, m, n = 0) = 0 o(S, m <= 0, n >= 1) = 0
和 e(S, m, n < 0) = 0 e(S, m, n = 0) = 1 e(S, m <= 0, n >= 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);
【讨论】:
以上是关于硬币交换变体的动态编程的主要内容,如果未能解决你的问题,请参考以下文章