我不会试图从理论上解释为什么会这样,我只是要遍历逻辑并描述它。
在您的示例中, nums = [1, 1, 1, 1, 1]
w=(S+sum(nums))//2 # in your example this is 4
dp=[0]*(w+1) # your array is 5 elements long, its max index is w
dp[0]=1 # initialize first index to 1, everything else is 0
for num in nums: # num will be 1 for 5 iterations
for j in range(w,num-1,-1): # j starts at the end and iterates backward.
# here j goes from index 4 to 0 every time
dp[j]=dp[j]+dp[j-num]. # remember dp = [1, 0, 0, 0, 0]. but after
# the first completion of the inner loop, dp
# will be [1, 2, 0, 0, 0]. remember you have
# negative indices in this language.
return dp[-1] # so after all iterations... dp is [1, 2, 3, 4, 5]
您只是将最初的 1 - 即 dp = [1, 0, 0, 0, 0] - 推向数组的末尾。而这种情况的发生方式取决于每次迭代时 num 的大小。
为什么会这样?让我们尝试一个不同的例子,看看对比。
假设 nums = [7, 13] 而 S 是 20。答案应该是 1,对吧? 7 + 13 = 20,这就是可能的全部。
w=(S+sum(nums))//2 # now this is 20.
dp=[0]*(w+1) # your array is 21 elements long, its max index is w
dp[0]=1 # initialize first index to 1, everything else is 0
for num in nums: # so num will be 7, then 13, 2 iterations
for j in range(w,num-1,-1): # j starts at the end and iterates backward.
# on first iteration j goes from index 20 to 6
# on 2nd iteration j goes from index 20 to 12
dp[j]=dp[j]+dp[j-num]. # so when num = 7, and you reach the 2nd last
# iteration of the inner loop... index 7
# you're gona set dp[7] to 1 via dp[7]+dp[0]
# subsequently, when num = 13... when you're
# at dp[20] you'll do dp[20] = d[20]+d[7].
# Thus now dp[20] = 1, which is the answer.
return dp[-1]
所以 num 越大……你跳过的越多,你将 1 移到顶部的次数就越少。较大的 num 值分散了可以加起来为给定 S 的可能组合。这个算法解释了这一点。
但请注意在最后一个示例中 dp[0] 处的 1 向上移动到 dp[20] 的方式。正是因为 7 + 13 = 20,唯一的组合。这些迭代和求和说明了所有这些可能的组合。
但是算法中如何考虑潜在的符号翻转?好吧,假设之前的 S 不是 20,而是 6。即 13 - 7 = 6,这是唯一的解决方案。嗯...让我们看看那个例子:
w=(S+sum(nums))//2 # now this is 13. (20+6)/2 = 13.
dp=[0]*(w+1) # your array is 14 elements long
dp[0]=1 # initialize first index to 1 again
for num in nums: # so num will be 7, then 13, 2 iterations
for j in range(w,num-1,-1):
# j starts at the end and iterates backward.
# on first iteration j goes from index 13 to 6
# on 2nd iteration j goes from index 13 to 12
dp[j]=dp[j]+dp[j-num].
# so this time, on the 2nd iteration, when
# num = 13... you're at dp[13]
# you'll do dp[13] = d[13]+d[0].
# Thus now dp[13] = 1, which is the answer.
# This time the 1 was moved directly from d0 to the end
return dp[-1]
所以这一次,当组合包含一个负数时,正是 w 的大小为 13 导致了解:索引 d[13] + d[0] = 1。数组大小更小,使得较大的数直接加1。
这很奇怪,我承认,你有 2 种不同的机制在工作,这取决于标志是什么,可能。在一个正数和负数相加为 S 的情况下,较小的整体数组长度导致 1 移动到最后一个插槽。对于 2 个正数...有这个中间人将发生的 1 向上移动。但至少你能看出区别……