题目: 给定一个正整数s, 判断一个数组arr中,是否有一组数字加起来等于s。
思路:
普通递归法:
还是老套路, 每个数字都有两种可能, 选和不选, 递归搞定, 注意2个跳出条件, 其中arr[i]>s 这个条件可有可无, 只是为了避免不必要的计算, 但是注意, 这个条件在递归中无所谓, 在迭代的动态规划中, 如果去除此条件, 注意防止数组越界, 同时将该位置置为false.
动态规划法:
我们需要一个二维数组subset[][],
subset长度
= arr[]的长度
,
subset[i]的长度
= 我们需要0~S的长度
也就是S+1
本例子中 S=9
如图我们可以看到, 我们利用subset构成了一个二维数组, 横坐标为S, 纵坐标为arr的索引值i (所以我们还需要利用 arr[i] 来找到真正的值), 我们需要反向
来看,
假设计算进行到了最后一步
, 以为如果进行到了最后一步还没出结果, 那么只有一种情况可以返回true, 就是arr[i]=S的剩余值, 也就是数组arr的最后一个数正好可以填上S的剩余值, 其他结果因为填不上, 所以全部为false.
我们继续前进到倒数第二步
, 如果进行到了倒数第二步, 还是同理, 因为i=1, arr[i]=34, 34>S, 那么显然, 这步走跟没走一样, 还是要看最后一步的结果, 所以我们直接将倒数第二步的每一列填上最后一步的计算结果.
最终我们会得到这个二维数组, 从中我们可以看出, 0~S, 也就是0~9中, 我们除了2都能凑出来, 同时因为中间计算结果被保存了下来, 避免了重复计算相同子问题, 这就是动态规划的优势.
0001000000
1001000000
1001100100
1001100100
1001110111
1011111111
true
其实就是反向的推出我们这个数字能凑出0~S
中的多少数, 并将其保存到中间结果中!
题解:
普通递归法:
public static boolean rec_subset(int i, int s) {
if (i == 0) return arr[i] == s;
if (s == 0) return true;
// 避免不必要的计算
if (arr[i] > s) return rec_subset(i - 1, s);
// 选i
boolean a = rec_subset(i - 1, s - arr[i]);
// 不选i
boolean b = rec_subset(i - 1, s);
return a || b;
}
动态规划法:
public static boolean dp_subset(int S) {
int[][] subset = new int[arr.length][S+1];
// 当s=0时, 为 true
for (int i = 0; i < arr.length; i++) {
subset[i][0]=1;
}
// 当找到数组最后一个数时, 如果arr[i]==s, 才为 true
for (int i = 0; i < S; i++) {
subset[0][i]=arr[0]==i?1:0;
}
// 开始将中间计算结果保存到subset中
for (int i = 1; i < arr.length; i++) {
for (int s = 1; s < S+1; s++) {
// 如果arr[i]大于s 直接跳过, 等于上一个(i-1)的计算结果
if (arr[i] > s) {
subset[i][s] = subset[i - 1][s];
} else {
// 选i
int a = subset[i - 1][s - arr[i]];
// 不选i
int b = subset[i - 1][s];
// 将计算结果插入到subset中
subset[i][s] = a + b > 0 ? 1 : 0;
}
}
}
// 返回右下角的值
return subset[subset.length-1][subset[0].length-1]==1;
}
main()
static int[] arr = {3, 34, 4, 12, 5, 2};
public static void main(String[] args) {
System.out.println(rec_subset(arr.length - 1, 11));
System.out.println(rec_subset(arr.length - 1, 12));
System.out.println(rec_subset(arr.length - 1, 13));
System.out.println(rec_subset(arr.length - 1, 197));
System.out.println("--------------------------");
System.out.println(dp_subset( 11));
System.out.println(dp_subset( 12));
System.out.println(dp_subset( 13));
System.out.println(dp_subset( 197));
}