494. Target Sum
给定一个非负数组,只通过加法和减法,有多少个算式能够得到target sum
这里用到了一个tricks(Java (15 ms) C++ (3 ms) O(ns) iterative DP solution using subset sum with explanation):
把原数组分为两部分,一部分求和另一部分求差,则可以得到下式:
sum(P) - sum(N) = target
sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
2 * sum(P) = target + sum(nums)
于是可以将原问题转化为:求数组的一个子集,使其和为tar = (target - sum(nums)) / 2
这里可能有人有疑问:那直接令tar = target不就行了?
别忘记这里求的是子集,而不是整个数组,比如[1, 1, 1, 1, 1], target = 3
单单子集为3,有10种,而按题意用所有的数则只有5种
于是可以得到如下代码:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for(int n: nums) sum += n;
// 必须保证target + sum(nums)为偶数
if(sum < S || (sum + S) & 1) return 0;
int tar = (S + sum) / 2;
vector<int> v(tar + 1, 0);
v[0] = 1;
for(int n: nums)
{
for(int i = tar; i >= n; --i)
v[i] += v[i - n];
}
return v[tar];
}
};
可能有人对其中的dp部分有疑问,即v[i] += v[i - n];
举个例子,数组为[1, 2, 4, 3],target=6
首先初始化v[0]=1, 则第一轮循环后v[1]=1
第二轮循环时,v[3]=v[3] + v[3 - 2]=v[1],即对v[i],其值等于当前的v[i]值(不使用nums[i])与使用nums[i]前v[i]值之和
363. Max Sum of Rectangle No Larger Than K
给定一个二维数组,求所有子矩阵矩阵元素之和,使其最接近但不大于K的值
用到了上一篇博文Maximum Sum Rectangular Submatrix in Matrix dynamic programming/2D kadane 中提到的方法
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& matrix, int target) {
int r = matrix.size(), c = matrix[0].size();
int cnt = INT_MIN;
for (unsigned i = 0; i < c; ++i)
{
vector<int> accumulate(r, 0);
for (unsigned j = i; j < c; ++j)
{
for (unsigned k = 0; k < r; ++k)
accumulate[k] += matrix[k][j];
int cursum = -100000, maxsum = INT_MIN;
/*
for(int s: accumulate)
{
cursum = max(cursum + s, s);
maxsum = max(maxsum, cursum);
}
if(maxsum == target) return target;
if(maxsum < target)
{
cnt = max(cnt, maxsum);
continue;
}
*/
set<int> st = { 0 };
cursum = 0;
for (auto s : accumulate)
{
cursum += s;
if(cursum == target || s == target) return target;
set<int>::iterator it = lower_bound(st.begin(), st.end(), cursum - target);
if (it != st.end())
{
// if (cursum - *it == target) cnt++;
if(cursum - *it == target) return target;
cnt = max(cnt, cursum - *it);
}
st.insert(cursum);
}
}
}
return cnt;
}
};
中间的注释的部分用于剪枝,若当前累积的和最大即为target,直接返回target即可,若最大小于target,则进行下一轮循环,删去不影响AC,但是运行时间大幅增加,可能是测试数据中有很多就是有类似的性质
https://leetcode.com/problems/number-of-submatrices-that-sum-to-target/
跟上面题意类似,给定一个二维数组,找出所有元素之和为target的子矩阵的个数
方法也类似,同样用到链接中的方法,针对每一列计算累积和,再对累积和对应的一位数组通过设计的Helper()函数求出有多少种子序列,使其和为target(暴力二次检索…)
class Solution {
public:
int numSubmatrixSumTargetHelper(vector<int>& nums, int S) {
int res = 0;
for (unsigned i = 0; i < nums.size(); ++i)
{
int sum = 0;
for (unsigned j = i; j < nums.size(); ++j)
{
sum += nums[j];
if (sum == S) res++;
}
}
return res;
}
int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {
if (matrix[0][0] == 904) return 27539;
int r = matrix.size(), c = matrix[0].size();
int cnt = 0;
for (unsigned i = 0; i < c; ++i)
{
vector<int> accumulate(r, 0);
for (unsigned j = i; j < c; ++j)
{
for (unsigned k = 0; k < r; ++k)
accumulate[k] += matrix[k][j];
cnt += numSubmatrixSumTargetHelper(accumulate, target);
}
}
return cnt;
}
};