0%

数组累加得到目标值

题目

给定一个正整数数组 arr 和一个整数 target , 如果可以任意选择 arr 中的数字, 求能否累加得到 target .

题解

暴力解法

穷举所有可能的和, 判断是否含有 target , 穷举过程如下, 其中中间变量格式为 (i, sum) , i 为索引, sum 为当前的累加和, 左支表示累加 arr[i] , 右支表示跳过该值.

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SumToTarget {
public static boolean isSum(int[] arr, int i, int sum, int target) {
if (i == arr.length)
return sum == target;

// 左分支 || 右分支
return isSum(arr, i + 1, sum + arr[i], target) || isSum(arr, i + 1, sum, target);
}

public static void main(String[] args) {
int[] arr = {3, 2, 7};
int target = 10;
System.out.println(isSum(arr, 0, 0, target));
}
}
Python
1
2
3
4
5
6
7
8
9
10
11
12
def sum_to_target(arr, i, cur_sum, target):
if i == len(arr):
return cur_sum == target

return sum_to_target(arr, i + 1, cur_sum + arr[i], target) or \
sum_to_target(arr, i + 1, cur_sum, target)


if __name__ == '__main__':
arr = [2, 3, 7]
target = 10
print(sum_to_target(arr, 0, 0, target))

动态规划

将暴力递归改进为动态规划时, 无需考虑原题目的含义, 只需找到迭代时各变量之间的依赖关系即可. 递归到动态规划的转化方法:

  1. 递归函数有 n 个参数, 就定义一个 n 维的数组
  2. 数组的下标是递归函数参数的取值范围
  3. 数组元素的值是递归函数的返回值, 这样就可以从边界值开始, 逐步填充数组,相当于计算递归函数值的逆过程。
1
return isSum(arr, i + 1, sum + arr[i], target) || isSum(arr, i + 1, sum, target);

由上述代码知递归函数第 i 次递归的结果只依赖于 i+1sum , 故需定义一个 i+1sum 状态关系的数组. 递归函数参数 i 的取值范围为 0~arr.length , sum 的范围为全部元素都不取到全部元素的和, 以 [3, 2, 7] 为例即为 0~12 . 构成的数组如下:

任意一格 (i, sum) 的值仅由 (i+1, sum)(i+1, sum+arr[i]) 决定. 如上表所示, (1, 3) 的值取决于 (1+1, 3)(1+1, 3+arr[1]) , 其中 arr[1] 为 2, 故 (1, 3) 的真值取决于 (2, 3)(2, 5) 的真值.

最终结果返回表中 (0, 0) 位置即可.

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SumToTarget {
public static boolean isSum(int[] arr, int target) {
// 本题只要有一列达到 target 即可, 故 target + 1 列即可
boolean[][] dp = new boolean[arr.length + 1][target + 1];
// target 列必为 True
dp[i][target] = true;

for (int i = arr.length - 1; i >= 0; i--) {
for (int j = target; j >= 0; j--) {
dp[i][j] = dp[i + 1][j];
if (j + arr[i] <= target) {
dp[i][j] = dp[i][j] || dp[i + 1][j + arr[i]];
}
}
}
return dp[0][0];
}
}
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def sum_to_target(arr, target):
row = len(arr)
dp = [[False] * (target + 1) for _ in range(row + 1)]

dp[row][target] = True

for i in range(row - 1, -1, -1):
for j in range(target, -1, -1):
if j + arr[i] <= target:
dp[i][j] = dp[i + 1][j] or dp[i + 1][j + arr[i]]
else:
dp[i][j] = dp[i + 1][j]

return dp[0][0]