题目链接:
题目大意:
给定一个由非负整数组成的长度为 n 的数组 nums 和一个目标整数 S, 需要对数组 nums 中的各个整数执行 + 或 - 的运算操作, 求出 使 nums 中的所有整数的运算结果等于 S 的 运算模式 的数目 ;
假设 1 <= n <= 20, 且 nums 中的数的和不大于 1000 ; 同时, 输出(符合要求的运算模式的数目) 保证可以用 32位整型表示 ;
例如: 给定: nums = [1, 1, 1, 1, 1], S = 3, 则输出应为: 5 ; (原因如下)
-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
解题过程:
(1) 最直接的思路便是 穷举式 的做法, 且题目已经对输入数据规模做了限制, 可以知道开销再大也大不到哪儿去, 于是直接考虑原始的穷举, 也即针对每个数, 逐次记录所有可能的运算操作的和, 我当时直接用 逐次双倍增长 的 vector, 最坏的情况下显然便是增长到 2^20 个结果了, 然后统计结果输出即可 ;
(2) 然后发现超时, 于是考虑稍微进行优化, 比如, 一次性分配好空间 等, 还是超时, 应该是因为测试样例颇多, 且这种做法确实慢 ... 然后 leetcode 可能算的是总的时间 ... ;
(3) 考虑到 无论何种运算模式, 最后的结果必然落在 各数之和的正负区间 之间, 显然计算过程中会有相同的和 (比如: 1+2=2+1=3), 而随着规模的不断翻倍, 这种浪费会爆炸开来的 ... , 故而可以考虑用 map 之类的结构及时收敛, 即, 每次只记录已处理的数的可能的和的出现次数, 则到最后便同样可以得到结果 ;
(*) 这种题目, 也有巧妙的解法, 关键是找出数学规律 ; 比如这个问题里的关系可以直观地转化为: 将 数组 nums 分成两个不相交的子集 a, b, 而 S = sum(a) - sum(b), 然后慢慢推导出其他的规律, 具体的直接搜索就可以找到分享了 ;
代码如下:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
using Ways = unordered_map<int, int>;
Ways ways;
ways[0] = 1;
for (int n : nums) {
Ways newWays;
for (auto w : ways) {
auto sum = w.first;
auto count = w.second;
newWays[sum + n] += count;
newWays[sum - n] += count;
}
ways = newWays;
}
return ways[S];
}
};
Runtime: 116 ms
题目链接:
题目大意:
给定一个由非负整数组成的长度为 n 的数组 nums 和一个目标整数 S, 需要对数组 nums 中的各个整数执行 + 或 - 的运算操作, 求出 使 nums 中的所有整数的运算结果等于 S 的 运算模式 的数目 ;
假设 1 <= n <= 20, 且 nums 中的数的和不大于 1000 ; 同时, 输出(符合要求的运算模式的数目) 保证可以用 32位整型表示 ;
例如: 给定: nums = [1, 1, 1, 1, 1], S = 3, 则输出应为: 5 ; (原因如下)
-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
解题过程:
(1) 最直接的思路便是 穷举式 的做法, 且题目已经对输入数据规模做了限制, 可以知道开销再大也大不到哪儿去, 于是直接考虑原始的穷举, 也即针对每个数, 逐次记录所有可能的运算操作的和, 我当时直接用 逐次双倍增长 的 vector, 最坏的情况下显然便是增长到 2^20 个结果了, 然后统计结果输出即可 ;
(2) 然后发现超时, 于是考虑稍微进行优化, 比如, 一次性分配好空间 等, 还是超时, 应该是因为测试样例颇多, 且这种做法确实慢 ... 然后 leetcode 可能算的是总的时间 ... ;
(3) 考虑到 无论何种运算模式, 最后的结果必然落在 各数之和的正负区间 之间, 显然计算过程中会有相同的和 (比如: 1+2=2+1=3), 而随着规模的不断翻倍, 这种浪费会爆炸开来的 ... , 故而可以考虑用 map 之类的结构及时收敛, 即, 每次只记录已处理的数的可能的和的出现次数, 则到最后便同样可以得到结果 ;
(*) 这种题目, 也有巧妙的解法, 关键是找出数学规律 ; 比如这个问题里的关系可以直观地转化为: 将 数组 nums 分成两个不相交的子集 a, b, 而 S = sum(a) - sum(b), 然后慢慢推导出其他的规律, 具体的直接搜索就可以找到分享了 ;
代码如下:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
using Ways = unordered_map<int, int>;
Ways ways;
ways[0] = 1;
for (int n : nums) {
Ways newWays;
for (auto w : ways) {
auto sum = w.first;
auto count = w.second;
newWays[sum + n] += count;
newWays[sum - n] += count;
}
ways = newWays;
}
return ways[S];
}
};
Runtime: 116 ms