往期文章 :
- LeetCode 剑指 Offer II 回溯(上) 专题总结
- LeetCode 剑指 Offer II 回溯(下) 专题总结
- LeetCode 剑指 Offer II 动态规划(一) 专题总结
- LeetCode 剑指 Offer II 动态规划(二) 专题总结
- LeetCode 剑指 Offer II 动态规划(三) 专题总结
目录
后两道都是0-1背包问题
100. 三角形中最小路径之和
题目:
给定一个三角形 triangle
,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
示例:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
提示:
1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104
思路:
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
int m = n; // 最底层列数
vector<int> dp(m, 0);
for(int i = 0; i < n; i++) {
for(int j = i; j >= 0; j--) {
if(j == 0) {
dp[0] += triangle[i][j];
continue;
}
if(j == i) {
dp[j] = dp[j-1] + triangle[i][j];
continue;
}
dp[j] = min(dp[j-1], dp[j]) + triangle[i][j];
}
}
return *min_element(dp.begin(), dp.end());
}
};
101. 分割等和子集
题目:
给定一个非空的正整数数组 nums
,请判断能否将这些数字分成元素和相等的两部分。
示例:
输入:nums = [1,5,11,5]
输出:true
解释:nums 可以分割成 [1, 5, 5] 和 [11] 。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
思路:
二维数组
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
int sum = 0, maxNum = 0;
for(auto& num : nums) {
sum += num;
maxNum = max(maxNum, num);
}
int target = sum / 2;
if(sum & 1 || target < maxNum) {
return false;
}
vector<vector<bool>> dp(n, vector<bool>(target+1, false));
for(int i = 0; i < n; i++) {
dp[i][0] = true;
}
dp[0][nums[0]] = true;
for (int i = 1; i < n; i++) {
for (int j = 1; j <= target; j++) {
if (j >= nums[i]) {
dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i]];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n-1][target];
}
};
一维数组
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
int sum = 0, maxNum = 0;
for(auto& num : nums) {
sum += num;
maxNum = max(maxNum, num);
}
int target = sum / 2;
if(sum & 1 || target < maxNum) {
return false;
}
vector<bool> dp(target, false);
dp[0] = true;
for (int i = 1; i < n; i++) {
for (int j = target; j >= nums[i]; j--) {
dp[j] = dp[j] | dp[j - nums[i]];
}
}
return dp[target];
}
};
102. 加减的目标值
题目:
给定一个正整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
- 例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例:
输入: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
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
思路:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for (auto& n : nums) {
sum += n;
}
if ((target + sum) & 1 != 0 || sum < target) {
return 0;
}
vector<int> dp((target + sum) / 2 + 1, 0);
dp[0] = 1;
for (auto& n : nums) {
for (int j = dp.size() - 1; j >= n; --j) {
dp[j] += dp[j - n];
}
}
return dp.back();
}
};