题目地址:
https://www.lintcode.com/problem/k-sum/description
给定一个正整数数组 A A A,再给定一个正整数 s s s和一个目标值 t t t,问从这个数组中有多少种方法取出 s s s个数其和为 t t t。
思路是动态规划。设
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]为从数组
A
A
A的前
i
i
i个数里取出
j
j
j个数,其和为
k
k
k的方案数,其中允许从前
0
0
0个数里取,也允许取
0
0
0个数。那么显然,取
0
0
0个数和为
0
0
0的方案数是
1
1
1种,所以
f
[
i
]
[
0
]
[
0
]
=
1
f[i][0][0]=1
f[i][0][0]=1。接下来我们考虑递推关系。对于从前
i
i
i个数里取出
j
j
j个数和为
k
k
k的方案,我们可以分为两部分:
1、该方案不包含
A
[
i
−
1
]
A[i-1]
A[i−1],那么这种情况的方案总数为
f
[
i
−
1
]
[
j
]
[
k
]
f[i-1][j][k]
f[i−1][j][k];
2、该方案包含
A
[
i
−
1
]
A[i-1]
A[i−1],那么这种情况的方案总数为
f
[
i
−
1
]
[
j
−
1
]
[
k
−
A
[
i
−
1
]
]
f[i-1][j-1][k-A[i-1]]
f[i−1][j−1][k−A[i−1]],而这种情况的方案只在
k
≥
A
[
i
−
1
]
k\ge A[i-1]
k≥A[i−1]时存在。
所以
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]就是上面两种方案数的总和,即:
f
[
i
]
[
j
]
[
k
]
=
f
[
i
−
1
]
[
j
]
[
k
]
+
f
[
i
−
1
]
[
j
−
1
]
[
k
−
A
[
i
−
1
]
]
f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k-A[i-1]]
f[i][j][k]=f[i−1][j][k]+f[i−1][j−1][k−A[i−1]]最后只需要返回
f
[
l
A
]
[
s
]
[
t
]
f[l_A][s][t]
f[lA][s][t]。初始条件,
∀
i
,
f
[
i
]
[
0
]
[
0
]
=
1
\forall i, f[i][0][0]=1
∀i,f[i][0][0]=1,
∀
s
>
0
,
f
[
0
]
[
0
]
[
s
]
=
0
\forall s > 0,f[0][0][s]=0
∀s>0,f[0][0][s]=0。代码如下:
public class Solution {
/**
* @param A: An integer array
* @param k: A positive integer (k <= length(A))
* @param target: An integer
* @return: An integer
*/
public int kSum(int[] A, int k, int target) {
// write your code here
// 判空
if (A == null || A.length == 0) {
return 0;
}
int[][][] dp = new int[A.length + 1][k + 1][target + 1];
for (int i = 0; i <= A.length; i++) {
dp[i][0][0] = 1;
}
for (int i = 0; i <= A.length; i++) {
for (int j = 0; j <= k; j++) {
for (int l = 0; l <= target; l++) {
// 前i个物品里选j个,如果i < j那一定选不出来,跳过
if (i < j) {
continue;
}
if (i > 0) {
dp[i][j][l] = dp[i - 1][j][l];
}
if (i > 0 && j > 0 && l >= A[i - 1]) {
dp[i][j][l] += dp[i - 1][j - 1][l - A[i - 1]];
}
}
}
}
return dp[A.length][k][target];
}
}
时空复杂度 O ( n k t ) O(nkt) O(nkt),其中 n n n是数组长度。