我有兴趣解决硬币交换问题的变种。回想一下硬币交换问题的正式定义:
给定值 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]);
这适用于某些示例案例,但它不起作用。任何提示?
答案 0 :(得分:0)
您需要在递归中添加一个标志,该标志是目前使用的加数数量的奇偶校验。
使用加数时,您可以翻转标记(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);
}