经典线性dp
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
for(int i = 1; i < n; i++) { //处理边界,以防stack overflow
triangle[i][0] += triangle[i-1][0];
triangle[i][i] += triangle[i-1][i-1];
}
for(int i = 2; i < n; i++)
for(int j = 1; j < i; j++)
triangle[i][j] += min(triangle[i-1][j],triangle[i-1][j-1]);
int res = 0x3f3f3f3f;
for(int i = 0; i < n; i++) res = min(res,triangle[n-1][i]);
return res;
}
};
- 容易想到若 n u m s nums nums 的总和为奇数,或最大数大于和的一半( t a r g e t target target),一定为 f a l s e false false。
- 若总和为偶数,接下来就是找是否有子序列的和为 t a r g e t target target,这里就需要用到 D P DP DP 方程 f [ i ] ∣ = f [ i − n u m ] f[i] \quad |= \quad f[i-num] f[i]∣=f[i−num] ,其中 f [ i ] f[i] f[i] 表示 i i i 是否存在。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
int total = 0, maxx = -0x3f3f3f3f;
for(const int &num: nums) {
total += num;
maxx = max(maxx,num);
}
if(total % 2 || total/2 < maxx) return false;
int target = total / 2;
vector<int> f(target+1, 0);
f[0] = true;
for(int i = 0; i < n; i++) {
int num = nums[i]; //将nums[i] 换成num速度快了一倍。。
for(int j = target; j >= num; j--)
f[j] |= f[j-num];
}
return f[target];
}
};
- n e g neg neg 表示添加 − - − 的整数之和, s u m sum sum 为 n u m s nums nums 所有数之和,那么 ( s u m − n e g ) − n e g (sum-neg)-neg (sum−neg)−neg 则为添加 + + + 的整数之和。
- 那么可以推导出: ( s u m − n e g ) − n e g = s u m − 2 ∗ n e g = t a r g e t (sum-neg)-neg = sum - 2 * neg = target (sum−neg)−neg=sum−2∗neg=target。进而推导出: n e g = ( s u m − t a r g e t ) / 2 neg = (sum-target) / 2 neg=(sum−target)/2。
- 所以发现若 n u m s nums nums 存在子序列之和为 n e g neg neg 那么就可以构造结果为 t a r g e t target target 的表达式。问题进而转化成:找出 n u m s nums nums 的子序列之和为 n e g neg neg 的子序列总数。
- 这个问题由 D P DP DP 来解决,设 f [ i ] [ j ] f[i][j] f[i][j] 为 n u m s nums nums 前 i i i 个元素和为 j j j 的子序列个数。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int total = 0, n = nums.size();
for(int num: nums) total += num;
int diff = total - target;
if(diff < 0 || diff % 2) return 0; //若target大于total 或 diff为奇数则直接返回
int neg = diff / 2; //neg = (total - target) / 2
vector<vector<int>> f(n+1, vector<int>(neg+1, 0));
f[0][0] = 1;
for(int i = 1; i <= n; i++) {
int num = nums[i-1];
for(int j = 0; j <= neg; j++) {
if(j < num) {
f[i][j] = f[i-1][j];
} else {
f[i][j] = f[i-1][j] + f[i-1][j-num];
}
}
}
return f[n][neg];
}
};
优化成一维空间
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int total = 0, n = nums.size();
for(int num: nums) total += num;
int diff = total - target;
if(diff < 0 || diff % 2) return 0;
int neg = diff / 2;
vector<int> f(neg+1, 0);
f[0] = 1;
for(int i = 1; i <= n; i++) {
int num = nums[i-1];
for(int j = neg; j >= num; j--) {
f[j] += f[j-num];
}
}
return f[neg];
}
};