题目 给你一个数组arr,和一个整数aim。如果可以任意选择arr中的数字,能不能累加得到aim,返回true或者false。
思路 【暴力递归】类似于子序列问题,遍历数组,当前数值可以选择要它,或者不要它。
【动态规划】设计一张二维dp表,行代表arr中的每一个数的位置,共arr.length + 1行(包含了越界位);若数组中全部元素的加和为sum,那么列代表范围是0~sum的加和。根据basecase可以看出,当sum=aim时,返回true,所以dp中最后一行(越界位)dp[arr.length][aim] = true,同一行的其他位置为false.根据最后一行的信息,从下往上每一行开始计算。根据暴力递归的步骤可以看出,dp[arr.lenght - 1][0]位置依赖于dp[arr.lenght][0]和dp[arr.lenght][0+arr[arr.lenght - 1]],其他同理,注意边界的条件。
package algorithm.section8;
public class Sum {
public static boolean isSum1(int[] arr, int i, int sum, int aim) {
if (i == arr.length) return sum == aim;
return isSum1(arr, i + 1, sum, aim) || isSum1(arr, i + 1, sum + arr[i], aim);
}
public static boolean isSum2(int[] arr, int aim) {
int sum = 0;
for (int value : arr) sum += value;
if (sum < aim) return false;
boolean[][] dp = new boolean[arr.length + 1][sum + 1];
dp[arr.length][aim] = true;
for (int i = dp.length - 2; i >= 0; i--)
for (int j = 0; j < dp[0].length; j++) {
if (dp[i + 1][j]) dp[i][j] = true;
if (j + arr[i] < dp[0].length)
dp[i][j] = (dp[i + 1][j] || dp[i + 1][j + arr[i]]);
}
return dp[0][0];
}
public static void main(String[] args){
int[] arr = {2, 3, 5};
int aim = 7;
System.out.println(isSum1(arr, 0, 0, aim));
System.out.println(isSum2(arr, aim));
}
}