「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」
题目
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。 返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2
输入: nums = [1], target = 1
输出: 1
题解
暴力搜索
枚举 数组;数组内任意元素执行加法或者减法,将计算结果与数组下一位元素在进行加法或或者减法;这个思路就递归;
如果数组内所有元素已经执行完,如果数组内所有元素计算结果为 ;完成目标值表达式数目+1
时间复杂度:
var findTargetSumWays = function (nums, S) {
let result = 0;
dfs(0, 0);
return result;
function dfs(idx, t) {
if (idx === nums.length) {
if (t === S) result++;
return;
}
dfs(idx + 1, t + nums[idx]);
dfs(idx + 1, t - nums[idx]);
}
};
动态规划
暴力搜索时间复杂度达到 ;时间复杂度太高了,在搜索过程中会有很多重复计算。加入这些重复计算可以保留下来,是不是就可以降低时间复杂度
可以将 dp[ i ][ j ]定义为从数组 nums 中 0 - i 的元素进行加减可以得到 j 的方法数量
举个例子:
- 数组总和
- 数组串联起来所有可能的表达式在区间上,
- 所以在 数组中,的区间是,的区间是,
- 又因为数组下标没有负数,所以需要将数组右移
- ,解释:个数,表达式结果为的方案数为
- ,解释:个数,计算表达式结果为 区间任意数的可能,并记录到 中
- ,解释:个数,计算表达式结果为 区间任意数的可能,并记录到 中
因为每个数值只能加或者减,是不是可以得到状态转移方程:
根据状态方程编辑代码如下:
var findTargetSumWays = function (nums, S) {
const len = nums.length;
const t = nums.reduce((a, b) => Math.abs(a) + Math.abs(b));
//动态规划
const dp = [];
for (let i = 0; i <= len; i++) {
dp[i] = Array(2 * t + 1).fill(0);
}
if (Math.abs(S) > Math.abs(t)) return 0;
dp[0][0 + t] = 1;
for (let i = 1; i <= len; i++) {
const c = nums[i - 1];
for (let j = -t; j <= t; j++) {
if (j - c + t >= 0) {
dp[i][j + t] += dp[i - 1][j - c + t];
}
if (j + c + t <= 2 * t) {
dp[i][j + t] += dp[i - 1][j + c + t];
}
}
}
//console.log('dp', dp)
return dp[len][S + t];
};