dp基础之难点解析K Sum

问题:给定数组A,包含n个互不相等的正整数,问有多少种方式从中找出K个数,使得和为arget
例:
A = [1,2,3,4].K = 2,Target = 5
输出:2(1,4;2,3)

问题分析:
考虑最后一步A[n-1]是否选入这K个数
    case1:A[n-1]不选入,则需要在前n-1个数里选K个数使得和为Target
    case2:A[n-1]选入,则需要在前n-1个数里选K-1个数使得和为Target-A[n-1]
    
子问题:设f[i][k][s]表示在前i个数里选出k个数使得和为s的方式数,
    f[i][k][s] = f[i-1][k][s] + f[i-1][k-1][s-A[i-1]](s>=A[i-1])
               = case1     +     case2
初始情况:
f[0][0][0] = 1,在前0个数里选出0个数使得和为0的方式数只有一种方法,即什么也不做
f[0][0][s] = 0,(s = 1,...,Target),在前0个数里选出0个数使得和大于0的方式数是0,即做不到
f[0][k][s] = 0,(k>0),前0个数选不出任何数来,因此是0

如果s<A[i-1],则只考虑case1,即不会把A[i-1]选入。

计算顺序:
    f[0][0,...,K][0,...,Target]
    .
    .
    .
    f[n][0,...,K][0,...,Target]

答案:f[n][K][Target]

时间复杂度O(n*k*Taregt),空间复杂度O(n*k*Target),可以优化到O(k*Target)

 

代码及注释如下:

 

def k_sum(A,K,Target):
    n = len(A)
    #f[i][k][s]表示在前i个数里选出k个数使得和为s的方式数
    f = [[[0 for t in range(Target+1)] for k in range(K+1)] for i in range(n+1)]
    #初始化f[0][0][0] = 1,f[0][0][s] = 0,(s = 1,...,Target),f[0][k][s] = 0,(k>0)
    f[0][0][0] = 1
    
    for i in range(1,n+1):
        for k in range(K+1):
            for s in range(Target+1):
                #f[i][k][s] = f[i-1][k][s] + f[i-1][k-1][s-A[i-1]](s>=A[i-1])
                #= case1
                f[i][k][s] = f[i-1][k][s]
                #case2
                if k > 0  and s>=A[i-1] :
                    f[i][k][s] += f[i-1][k-1][s-A[i-1]]
    return f[n][K][Target]
A = [1,2,3,4]
K = 2
Target = 5
print(k_sum(A,K,Target))
#输出:2

 

空间优化代码如下:

def k_sum(A,K,Target):
    n = len(A)
    #f[i][k][s]表示在前i个数里选出k个数使得和为s的方式数
    f = [[[0 for t in range(Target+1)] for k in range(K+1)] for i in range(2)]
    #初始化f[0][0][0] = 1,f[0][0][s] = 0,(s = 1,...,Target),f[0][k][s] = 0,(k>0)
    f[0][0][0] = 1
    #滚动数组,用new替换i,old替换i-1,每次i循环一次,old和new互换,
    #为了保证i循环第一次old = 0,new = 1,故把old的初值设为1,new设为0
    old,new = 1,0
    for i in range(1,n+1):
        old,new = new,old
        for k in range(K+1):
            for s in range(Target+1):
                #f[i][k][s] = f[i-1][k][s] + f[i-1][k-1][s-A[i-1]](s>=A[i-1])
                #= case1
                f[new][k][s] = f[old][k][s]
                #case2
                if k > 0  and s>=A[i-1] :
                    f[new][k][s] += f[old][k-1][s-A[i-1]]
    return f[new][K][Target]
A = [1,2,3,4]
K = 2
Target = 5
print(k_sum(A,K,Target))
#输出:2

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值