求出一个数组arr中有没有那几个数加起来等于S,有,返回true 没有返回false。
分析:
eg:arr={2,4,6,3} S=10;
动态规划可以这么理解:1、很多个重叠子问题 2、选或者不选的问题
对于数组中的每一个数,比如例子中的3,如果选择,则需要3之前的数可以组成S-3=7
按下标i表示:
如果选:f(i,S)=f(i-1,S-arr[i]);
如果不选:f(i)=f(i-1,S)
再考虑问题的出口:当数组只有一个元素,此时返回arr[0]==S;
当S=0,即f(i,S)中S为0,说明在i的后面的数组中已经有加起来等于S的数,则返回true,
如果arr[i]>S ,则直接计算f(i-1,S)即可。
动态规划可以用递归的方式完成,但复杂度为O(2^n),因为它是由大到小的推,比如要算f(i),要算f(i-1),而动态规划是从小到大,先计算f(i),再计算f(i+1),把每次的结果存储下来,最后复杂度变为O(n)。
下面是代码实现:rec_subSet是递归实现,dp_subSet是动态规划实现。
package test;
public class Main {
public static boolean rec_subSet(int[] arr,int i,int S){
if(S==0)
return true;
if(i==0)
return arr[i]==S;
if(arr[i]>S){
return rec_subSet(arr,i-1, S);
}
return rec_subSet(arr, i-1, S-arr[i])||rec_subSet(arr, i-1, S);
}
public static void dp_subSet(int[] arr,int S) {
boolean[][] subSet = new boolean[arr.length][S+1];
for(int i = 0; i <= arr.length-1; i++){
subSet[i][0] = true;
}
for(int i = 0; i <= S; i++){
subSet[0][i] = false;
if(i==arr[0])
subSet[0][arr[0]] = true;
}
for(int i = 1; i <= arr.length-1;i++){
for(int s = 1;s<=S;s++){
if(arr[i]>s)
subSet[i][s] = subSet[i-1][s];
else {
subSet[i][s] = (subSet[i-1][s-arr[i]]) || (subSet[i-1][s]);
}
}
}
System.out.println(subSet[5][S]);
}
public static void main(String[] args) {
int[] arr = {3,34,4,12,5,2};
int S = 9;
dp_subSet(arr, S);
System.out.println(rec_subSet(arr,arr.length-1,S));
}
}