动态规划:01背包理论基础
01 背包
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
动规五部曲:
1.i表示物品位置,j表示背包容量,dp[i][j]表示从0到i的物品,背包容量到校为j的所能装的价值总和。
2.初始化,对于dp[i][[0]初始化为0(因为背包容量为0,则必然无法装东西)
对于dp[0][j],如果j<weight[0]初始化为0,如果j>=weight[0],就是初始化为value[0].
3.递推公式,dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]).
4.遍历顺序,从前往后(从后往前也可),优先 遍历背包还是遍历物品也都行.
5.略。
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
int type ; int lunggage;
cin >> type >> lunggage;
vector<int> materialSpace(type);
vector<int> materialValue(type);
for(int i = 0; i < type; ++i) {
cin >> materialSpace[i];
}
for(int j = 0; j < type; ++j) {
cin >> materialValue[j];
}
vector<vector<int>> dp(type,vector<int> (lunggage+1,0));
for(int j = 0;j<=lunggage;j++)
{
if(materialSpace[0]<=j)
dp[0][j] = materialValue[0];
}
for(int i = 1;i<type;i++)
{
for(int j = 0;j<=lunggage;j++)
{
if(j<materialSpace[i])dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j],dp[i-1][j-materialSpace[i]] +materialValue[i]);
}
}
cout << dp[type - 1][lunggage] << endl;
return 0;
}
有些乱了。
然后还有一种滚动写法:
1.dp数组定义 j表示容量,dp[j]表示在j大小容量所能容纳的最大价值。
2.初始化,全部初始化为0,即不需要处理即可。
3.递推公式: dp[j] = max(dp[j],dp[j-weight[i]] +value[i]); 要么不采用这个材料,要么采用这个材料,背包容量数量降低。
4.递推顺序:倒序(防止同一物品被连续放入多次)
且先遍历物品嵌套遍历背包容量。
因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m ; int n;
cin >> m >> n;
vector<int> weight(m);
vector<int> value(m);
for(int i = 0; i < m; ++i) {
cin >> weight[i];
}
for(int j = 0; j < m; ++j) {
cin >> value[j];
}
vector<int> dp(n+1,0);
for(int i = 0;i<m;i++)
{
for(int j = n;j>=weight[i];j--)
{
dp[j] = max(dp[j],dp[j-weight[i]] +value[i]);
}
}
cout << dp[n] << endl;
return 0;
}
分割等和子集
给你一个 只包含正整数 的 非空 数组
nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割成两个元素和相等的子集。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
dp[i]:
01背包中,dp[j] 表示: 容量(所能装的重量)为j的背包,所背的物品价值最大可以为dp[j]。
如果背包所载重量为target, dp[target]就是装满 背包之后的总价值,因为 本题中每一个元素的数值既是重量,也是价值,所以,当 dp[target] == target 的时候,背包就装满了。
初始化为0 即可。
使用滚动数组,顺序与递推公式基本与上题一致。
cpp
class Solution {
public:
bool canPartition(vector<int>& nums) {
vector<int> dp (20001,0);
int sum = 0;
for(int i = 0 ;i<nums.size();i++)
{
sum += nums[i];
}
if(sum%2 == 1) return false;
int target = sum/2;
for(int i = 0;i< nums.size();i++)
{
for(int j = target;j>=nums[i];j--)
{
dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);
}
}
if(dp[target]==target)return true;
return false;
}
};