LeetCode
- 题目地址:https://leetcode.com/problems/target-sum/#/description
- 问题描述:给定一个数组,和一个目标数,数组中的每个数可以取+也可以取-,问加起来是目标数的途径有多少种
解题思路:
- 最直接的思路就是,对于数组的size=n,一共有2的n次方种组合,那么挨个试,如果求和等于目标数target,那么途径就加一,但是因为做了很多重复的运算,比如对于[1,2,3]来说,如果已经算了1+2,那么只需要再加3即可得到1+2+3,但是2的n次方种组合没有利用之前运算的结果,这种方法会超时
用深度优先搜索,消除很多重复运算。这种算法首先要做个变换,这里简单解释一下:设取正的数和为P,取负的数和为N,全取正总和为sum,目标数为s,则有
- P - N = s
- P + N = sum
可以提前算出来,那么满足条件的时候,P=(sum+s)/ 2=target,即sum+s一定是偶数,所以如果sum < s,那么肯定不存在,同时如果sum+s不为偶数那么也不存在,都返回0。所以对于所有的数组,挨个加入正数P中,如果满足条件则途径加1。因为是dfs,注意状态还原和递归的参数。但是dfs用时约600ms
dfs用了大量的递归,耗时很大。可以换一种方法计算。
int subsetSum(vector<int>& nums, int s) { int dp[s + 1] = { 0 }; dp[0] = 1; for (int n : nums) for (int i = s; i >= n; i--) dp[i] += dp[i - n]; return dp[s]; }
这段代码的大概意思是,dp[i]表示到使得和为i有多少种途径,dp[0]=1,显然
和为0的途径是都不选即1.然后每遇到一个数字n,就能使和i-n变成和i,所以
dp[i]+=dp[i-n],此时dp[i]代表n之前包括n的数字,使得和为i的途径有多少种。
因为如果先更新i小的,会影响后面的更新,所以先更新i大的,再更新小的。
代码:
- 思路1:
//超时
class Solution {
public:
bool check(vector<int>& nums, int &S, int n) {
int sum = 0;
int nCopy = n;
for (int index = 0; index < nums.size(); index++) {
//cout << index << " " << n << endl;
if (n % 2 == 1)
sum += nums[index];
else
sum -= nums[index];
n /= 2;
}
//cout << nCopy << " : " << sum << endl;
if (sum == S) return true;
else return false;
}
int findTargetSumWays(vector<int>& nums, int S) {
//1表示+,0表示-
int res = 0;
for (int i = 0; i < 1 << nums.size(); i++) {
if (check(nums,S,i))
res++;
}
return res;
}
};
- 思路2:
//600ms
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
//1表示+,0表示-
int res = 0;
//sum of nums + S = 2 * sum of +
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
if ((sum + S) % 2 == 1) return 0;
int sumplus = (sum + S) / 2;
//cout << sumplus << endl;
//dfs
int cur = 0;
return dfs(nums,cur,sumplus,0);
//return res;
}
int dfs(vector<int>& nums, int& cur, const int& target, int i) {
if (i == nums.size()) {
return cur == target ? 1 : 0;
}
//选第i个和不选第i个
int sum = 0;
sum += dfs(nums,cur,target,i+1); //不选
cur += nums[i];
sum += dfs(nums,cur,target,i+1); //选
cur -= nums[i]; //还原
return sum;
}
};
- 思路3:
//9ms
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int s) {
int sum = accumulate(nums.begin(), nums.end(), 0);
return sum < s || (s + sum) & 1 ? 0 : subsetSum(nums, (s + sum) >> 1);
}
int subsetSum(vector<int>& nums, int s) {
int dp[s + 1] = { 0 };
dp[0] = 1;
for (int n : nums)
for (int i = s; i >= n; i--)
dp[i] += dp[i - n];
return dp[s];
}
};