题目
类型:动态规划 深搜
难度:中等
版本1
暴搜 选与不选
时间复杂度
O
(
2
n
)
O(2^n)
O(2n)
class Solution {
public:
int n, res = 0;
void dfs(int u, vector<int>&nums, long long target){
if(u==n){
if(!target) res++;
return;
}
dfs(u+1, nums, target+nums[u]);
dfs(u+1, nums, target-nums[u]);
}
int findTargetSumWays(vector<int>& nums, int S) {
n = nums.size();
dfs(0, nums, (long long)S);
return res;
}
};
版本2
考虑到数组中的数都是正数,并且最大和最多为2000,所以所有数值的范围在
[
−
2000
,
2000
]
[-2000, 2000]
[−2000,2000]之间,使用一个偏移以后,数据规模在
[
0
,
4000
]
[0,4000]
[0,4000],远远小于
2
20
2^{20}
220
使用ways[i][j]来表示使用前i个数到达到j有多少种方式。初始值ways[0][0]=1表示和为0的方式有一种。后面从1开始计数。
时间复杂度
O
(
4000
∗
20
)
O(4000*20)
O(4000∗20)
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
//2020.4.23
//nums中全是正数
int sum = 0;
int n = nums.size();
for(auto c: nums) sum += c;
//产生的数据只可能在[-sum, sum]之间
if(sum < S) return 0;
const int offset = sum; //数组下标不能有负 所以要做一个偏移
vector<vector<int>> ways(n+1, vector<int>(offset+sum+1, 0));
ways[0][offset] = 1;//表示ways[0][0] = 1
for(int i = 0; i < n; i++){
for(int j = nums[i]; j + nums[i] <= 2*sum; j++){
if(ways[i][j]){ //如果不为空值
ways[i+1][j-nums[i]] += ways[i][j]; //下一个状态就可以由当前状态转换过去
ways[i+1][j+nums[i]] += ways[i][j];
}
}
}
return ways.back()[offset+S];
}
};
版本3
使用滚动数组,把二维的空间优化为一维。
在内层循环创建一个临时的一维数组,然后每次更新。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
//2020.4.23
//nums中全是正数
int sum = 0;
int n = nums.size();
for(auto c: nums) sum += c;
// //产生的数据只可能在[-sum, sum]之间
if(sum < S) return 0;
const int offset = sum; //数组下标不能有负 所以要做一个偏移
vector<int> ways(offset+sum+1, 0);
ways[offset] = 1;
for(auto num: nums){
vector<int> temp(2*sum+1);
for(int j = num; j + num <= 2*sum; j++){
if(ways[j]){
temp[j+num] += ways[j];
temp[j-num] += ways[j];
}
}
swap(ways, temp);
}
return ways[offset+S];
}
};