题意:
给定一个不重复的正整数数组和一个正整数,找出数组的数相加等于这个正整数的可能组合数。(组合中的数字可以重复,可以不必使用所有数组的数,不同顺序各算一种)
分析:
我们要得出之和的可能数,就是要搜索哪些组合的和是目标数。构造一棵树,递归搜索:(能得到正确答案,但是超时)
比如对于[1,2,3]目标数是4
4
3 (4-1) 2 (4-2) 1(4-3)
2 1 0 1 0 -1 0 -1 -2
以此类推,小于等于零的时候返回,等于零的就是我们要的。
超时算法:
public class Solution {
int count = 0;
int nums[];
public int combinationSum4(int[] nums, int target) {
this.nums = nums;
helper(target);
return count;
}
private void helper(int sum){
if(sum <= 0){
if(sum == 0){
count++;
}
return;
}
for(int i=0; i<nums.length; i++){
helper(sum - nums[i]);
}
}
}
那么我们尝试题目标签的动态规划法,其实和递归搜索一样都是一种思想
动态规划的每一个结点也等于下一层的节点的和。
比如: 4
3 (4-1) 2 (4-2) 1(4-3)
target为4的值就等于target为3,2,1的值的和。(因为数组有1,2,3),target每向下一层就减去一个数组的值,减去数组的值1,2,3结果分别为3,2,1,就是第二层的所有结点。第一层根节点(4)的值就是下一层所有子树(3,2,1)的值的和。
当选定1,2,3的时候就好像是排列组合的第一位被确定了。
和为4所有的情况:第一位为1,剩下3的情况数
第一位为2,剩下2的情况数
第一位为3,剩下1的情况数
public class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target+1]; //数组每一位代表一个结点,结点值是对应target对应的组合数
int sum = 0;
dp[0] = 1;
for(int i=1; i<dp.length; i++){ //纵向遍历每一层,i代表target
for(int j=0; j<nums.length; j++){ //横向遍历某一层
if (i - nums[j] >= 0) //大于等于0的是有效的节点,否则是无效的结点
sum += dp[i - nums[j]]; //这一层所有的(有效的)结点(代表的子树)的值的和就是这一层父节点的值
}
dp[i] = sum;
sum = 0;
}
return dp[target];
}
}