文章目录
- 动态规划(背包问题)
- 1. 01背包
- 2. 分割等和子集
- 3. 目标和
- 4. 最后一块石头的重量 ||
动态规划(背包问题)
1. 01背包
题目链接
-
状态表示
dp[i][j] 表示从前i个物品当中挑选,总体积不超过j,所有选法当中能挑选出来的最大价值
-
状态转移方程
-
初始化
-
填表
-
返回值
AC代码:
#include <iostream>
#include <cstring>
const int N = 1010;
int n, V, v[N], w[N];
int dp[N][N];
int main()
{std::cin>> n >> V;for (int i = 1; i <= n; i++){std::cin>> v[i] >> w[i];}for (int i = 1; i <= n; i++){for (int j = 0; j <= V; j++){dp[i][j] = dp[i - 1][j];if (j >= v[i]) {dp[i][j] = std::max(dp[i][j], w[i] + dp[i - 1][j - v[i]]);}}}std::cout << dp[n][V] << std::endl;memset(dp, 0, sizeof(dp));for (int j = 1; j <= V; j++) dp[0][j] = -1;for (int i = 1; i <= n; i++){for (int j = 0; j <= V; j++){dp[i][j] = dp[i - 1][j];if (j >= v[i] && dp[i - 1][j - v[i]] != -1) {dp[i][j] = std::max(dp[i][j], w[i] + dp[i - 1][j - v[i]]);}}}std::cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << std::endl;return 0;
}
空间优化:
利用滚动数组做空间上的优化,遍历顺序需要从右到左
不需要解释优化后的状态表示,以及状态转移方程
优化后代码:
#include <iostream>
#include <cstring>const int N = 1010;int n, V, v[N], w[N];
int dp[N];int main()
{std::cin>> n >> V;for (int i = 1; i <= n; i++){std::cin>> v[i] >> w[i];}for (int i = 1; i <= n; i++){for (int j = V; j >= v[i]; j--){dp[j] = std::max(dp[j], w[i] + dp[j - v[i]]);}}std::cout << dp[V] << std::endl;memset(dp, 0, sizeof(dp));for (int j = 1; j <= V; j++) dp[j] = -1;for (int i = 1; i <= n; i++){for (int j = V; j >= v[i]; j--){if (dp[j - v[i]] != -1) {dp[j] = std::max(dp[j], w[i] + dp[j - v[i]]);}}}std::cout << (dp[V] == -1 ? 0 : dp[V]) << std::endl;return 0;
}
2. 分割等和子集
题目链接
将一个数组分割成相同的两部分,就需要在整个数组里面找正好相等就可以。其实就是一个背包问题
-
状态表示
dp[i][j]表示 0 到 i 区间内正好等于是否可以满足正好等于 j
-
状态转移方程
-
初始化
第一列为true ,当目标是0是肯定可以满足
-
填表
-
返回值
AC代码:
class Solution
{
public:bool canPartition(vector<int>& nums) {int n = nums.size();int sum = 0;for (auto x : nums) sum += x;if (sum % 2) return false;int aim = sum / 2;vector<vector<bool>> dp(n + 1, vector<bool>(aim + 1));for (int i = 0; i <= n; i++) dp[i][0] = true;for (int i = 1; i <= n; i++){for (int j = 1; j <= aim; j++){dp[i][j] = dp[i - 1][j];if (j >= nums[i - 1]) dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i - 1]];}}return dp[n][aim];}
};
利用滚动数组进行优化:
class Solution
{
public:bool canPartition(vector<int>& nums) {int n = nums.size();int sum = 0;for (auto x : nums) sum += x;if (sum % 2) return false;int aim = sum / 2;vector<bool> dp(aim + 1);dp[0] = true;for (int i = 1; i <= n; i++){for (int j = aim; j >= nums[i - 1]; j--){dp[j] = dp[j] || dp[j - nums[i - 1]];}}return dp[aim];}
};
3. 目标和
题目链接
分析题目,a 代表所有正数的和,b则代表所有负数的和
a - b = target a + b = sum
所以a = (target + sum) / 2
所以最终求的是是否可以让这个数是a
-
状态表示
dp[i][j]表示从 i 个中选正好等于j 有多少中选法
-
状态转移方程
-
初始化
-
填表
-
返回值
AC代码:
class Solution
{
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0;for (auto x : nums) sum += x;int aim = (sum + target) / 2;if (aim < 0 || (sum + target) % 2) return 0;int n = nums.size();vector<vector<int>> dp(n + 1, vector<int>(aim + 1));dp[0][0] = 1;for (int i = 1; i <= n; i++){for (int j = 0; j <= aim; j++){dp[i][j] = dp[i - 1][j];if (j >= nums[i - 1]) {dp[i][j] += dp[i - 1][j - nums[i - 1]];}}}return dp[n][aim];}
};
4. 最后一块石头的重量 ||
题目链接
这个题目就是在一个数组当中选一些数字,让这些数字尽可能的接近sum / 2
-
状态表示
dp[i][j]表示 i 中选,总体积不超过j此时的最大和
-
状态转移方程
-
初始化
-
填表
-
返回值
AC代码:
class Solution {
public:int lastStoneWeightII(vector<int>& stones) {int sum = 0;for (auto x : stones) sum += x;int n = stones.size(), m = sum / 2;vector<vector<int>> dp(n + 1, vector<int>(m + 1));for (int i = 1; i <= n; i++) {for (int j = 0; j <= m; j++){dp[i][j] = dp[i - 1][j];if (j >= stones[i - 1]) {dp[i][j] = max(dp[i][j], dp[i - 1][j - stones[i - 1]] + stones[i - 1]);}}}return sum - 2 * dp[n][m];}
};
dp[i][j] = dp[i - 1][j];if (j >= stones[i - 1]) {dp[i][j] = max(dp[i][j], dp[i - 1][j - stones[i - 1]] + stones[i - 1]);}}}return sum - 2 * dp[n][m];
}
};