K Sum

Given n distinct positive integers, integer k (k <= n) and a number target.

Find k numbers where sum is target. Calculate how many solutions there are?

Example

Given [1,2,3,4], k = 2, target = 5.There are 2 solutions: [1,4] and [2,3].Return 2.

这是sum一系列问题中的终极版本,即求给出的n个数字中,取其中k个数字,最终和为target的组合有多少。

注意这题问的是有多少,并不是要求给出所有的符合条件的k个数组的全部组合。按照前面的2 sum, 3sum, 4sum等题推理如果采用排序加双指针夹逼的方法,复杂度是O(n^(k-1))的,如果采用4sum这种利用2sum hashmap的做法,则最优的复杂度为有降低,但是4sum这种已经很复杂,对于更大的k为偶数的情况,会变得特别复杂。以上分析可以看出DP是这时候的好选择。

首先看这题本身的描述,n个取k个,组成和为target的情况,其实看描述非常像背包问题,容量是这里的target,但是背包问题本身不要求知道一共是多少个物体组成,且不需要知道方案数,所以二维的DP无法解决。我们可以定义3维的DP状态:f[i][j][t]即前i个字母中取出j个能组成和为t的情况的种数。

定义转换状态:f[i][j][t] = f[i-1][j][t] + f[i-1][j-1][t-nums[i-1]]和背包问题一样,我们需要对取不取第i 个数字做一个判断。

初始化,这个是最tricky的地方,究竟如何初始化才对。首先数组中的所有的数字都是正数,且都不相同。所以f[i][1][A[i-1]]应该都是1. f[i][1][A[j]] = f[i-1][1][A[i]]+f[i-1][0][0], f[i-1][1][A[i]]肯定是0,所以f[i-1][0][0]的值是1.可以先进行初始化。

最后的结果是f[n][k][target].复杂度为O(k*n*target).上述空间复杂度可以采用滚动数组优化为二维的(在n这一维度进行优化)。

代码如下:

class Solution:
    """
    @param A: An integer array.
    @param k: a positive integer (k <= length(A))
    @param target: integer
    @return an integer
    """
    def kSum(self, A, k, target):
        n = len(A)
        dp = [[[0] * (target+1) for i in xrange(k+1)] for j in xrange(n+1)]
        for i in xrange(0, n+1):
            dp[i][0][0] = 1
        for i in xrange(1, n+1):
            for j in xrange(1, min(k+1,i+1)):  #j的范围一定要注意
                for t in xrange(1, target+1):
                    if t < A[i-1]:
                        dp[i][j][t] = dp[i-1][j][t]
                    else:
                        dp[i][j][t] = dp[i-1][j][t] + dp[i-1][j-1][t-A[i-1]]
        
        
        return dp[n][k][target]

这题三维的滚动数组优化比较困难,尝试了几次都有问题。后序再说。

这题难度比较高,面试中出现的概率比较低,但仍然是一道不错的DP题。

原文地址:https://www.cnblogs.com/sherylwang/p/5626291.html