给定值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。
我找到了3种方法HERE。但是无法理解空间优化的动态编程方法,其中仅使用单维数组表[]。
int count( int S[], int m, int n )
{
// table[i] will be storing the number of solutions for
// value i. We need n+1 rows as the table is consturcted
// in bottom up manner using the base case (n = 0)
int table[n+1];
// Initialize all table values as 0
memset(table, 0, sizeof(table));
// Base case (If given value is 0)
table[0] = 1;
// Pick all coins one by one and update the table[] values
// after the index greater than or equal to the value of the
// picked coin
for(int i=0; i<m; i++)
for(int j=S[i]; j<=n; j++)
table[j] += table[j-S[i]];
return table[n];
}
答案 0 :(得分:4)
尝试使用这种方式理解算法。
table[i][j]
表示使用第一个i
种类型的硬币来更改值j
。然后:
table[i][j] = table[i-1][j] + table[i][j-S[i]]
显然,在制作j
硬币时,您有两种选择。不使用第i个硬币或使用第i个硬币。不使用第i个硬币时,解决方案编号为table[i-1][j]
。当使用第i个硬币时,解数为table[i][j-S[i]]
,这意味着使用前i个硬币来组成jS [i]值。因此,总数是两者的总和,即table[i-1][j] + table[i][j-S[i]]
< / p>
在代码中,您将看到for循环。外部循环遍历i,内部循环遍历j。 +=
语句根据上面的等式计算table[i][j]
。
修改
您的代码中的 table[j]
实际上是我在上面讨论的table[i][j]
,i
是循环中的值。在循环table[N]
表示table[M][N]
之后,使用第一个M
硬币代表所有硬币,为N
创造价值。
我将根据代码提供更多详细信息:
for(int i=0; i<m; i++)
for(int j=S[i]; j<=n; j++)
table[j] += table[j-S[i]];
i = 0
时,table[j]
表示使用前1个硬币对值j
进行更改。例如,table[2]
现在意味着使用coins {1}
来更改2.所以:
table[1] = table[1] + table[1 - S[0]] = table[1] + table[0] = 1 + 0= 1
table[2] = table[2] + table[2 - S[0]] = table[2] + table[1] = 0 + 1= 1
table[3] = 1
table[4] = 1
之后,当i = 0时,我们得到了结果。table[1] ~ table[4]
现在意味着使用coin {1}
分别对值1,2,3,4进行更改。
当i = 1时,table[j]
表示使用coin {1, 2}
对值j
进行更改。
table[2] = table[2] + table[2 - S[1]] = table[2] + table[0] = 1 + 1= 2
table[3] = table[3] + table[3 - S[1]] = 1 + 1 = 2
table[4] = table[4] + table[4 - S[1]] = table[4] + table[2] = 1 + 2 = 3
以下过程是相同的。
最后,我们在table[4]
出来时i = 1
并分析它:
table[4] = table[4] + table[4 - S[1]] = table[4] + table[2] = 1 + 2 = 3
左边的table[4]
是我们正在计算的值,实际上是table[i=1][4]
。右侧的table[4]
代表前一个值,在本例中为table[i=0][4]
。它可以扩展到:
table[i=1][4] = table[i=0][4] + table[i=1][4 - S[1]]
方程式正好
table[i][j] = table[i-1][j] + table[i][j-S[i]]
编辑跟进问题
如果您认为自己真的理解这个问题,请尝试使用新约束来解决同样的问题。如果每枚硬币只能使用一次怎么办?例如,N = 4且S = {1,2,3},只有一个解{1,3},因此输出应为1.并且对于N = 10且S = {2,5,3,6},仍然只有一个解决方案{2,3,5},输出为1。
提示:原始代码只需更改一行即可。
答案 1 :(得分:2)
首先请注意,表[i]是N = i时硬币变换的方式数。
给定算法根据给定的硬币组(S [])填充此数组(table [])。 最初,table []中的所有值都初始化为0.而table [0]设置为0(这是基本情况N = 0)。
每枚硬币以下列方式在表格[]中累加值。
对于值为X的硬币,以下是对表[] -
的更新table [X] = table [X] + 1
这很容易理解。具体来说,这会添加解决方案{X}。
&gt; X 强>
表[Y] =表格[Y] +表格[Y-X]
这很难理解。例子X = 3,考虑Y = 4的情况。
4 = 3 + 1,即4可以通过组合3和1获得。并且根据定义,获得1的方式的数量是表[1]。因此,表[4]中添加了许多方法。这就是为什么上面的表达式使用表[Y-X]。
算法中的以下行代表相同的(以上两个步骤) -
table[j] += table[j-S[i]];
在算法结束时,table [n]包含n的解决方案。
答案 2 :(得分:0)
将尝试为他人进行解释。
考虑这段代码-
dp = [[0 for i in range(len(coins))] for j in range(n+1)]
for i in range(n+1):
for j in range(len(coins)):
if i == 0:
dp[i][j] = 1
elif j == 0:
dp[i][j] = 0
else:
dp[i][j] = dp[i][j-1]
if i - coins[j] >= 0:
dp[i][j] += dp[i-coins[j]][j]
print dp[n][len(coins)-1]
这种方法是非常基本的,没有空间优化。最初,我们可能认为我们只是从列索引j - 1
访问信息,所以我们可以删除列,但事实并非如此,因为我们也在访问dp[i - coins[j]][j]
。因此,j'th
索引中包含的信息很有用,我们不能删除这些列。
现在考虑一下,
dp = [[0 for i in range(n+1)] for j in range(len(coins))]
for i in range(len(coins)):
for j in range(n+1):
if j == 0:
dp[i][j] = 1
elif i == 0:
dp[i][j] = 0
else:
dp[i][j] = dp[i-1][j]
if j - coins[i] >= 0:
dp[i][j] += dp[i][j-coins[i]]
print dp[len(coins)-1][n]
我们所做的所有事情都与for循环的顺序相反。通过仔细观察,我们可以看到dp[i][j-coins[i]]
与dp[i][j]
来自同一行。那么这是否意味着我们不需要维护其他行的信息?是的,我们没有。
现在有两种解决方法,要么维护两个数组,一个用于dp[i]
,另一个用于dp[i-1]
,或者完全删除行索引,这将导致所有数据在{{ 1}}用于dp[j]
的所有值。
第二种方法的代码-
i