前言
今天复(yu)习背包问题。。。 其实之前背包问题,我也写过,但是属实写的太拉跨了,今天写一波重制版。。。
背包算是一个经典的dp问题了。大概思路是这样:用一个体积为 V 的背包,选择一系列物品装进去。每个物品有他的价值 W
问:在不超过背包最大体积的情况下,能够获取的物品最大价值是多少?
01背包
原题链接:https://www.acwing.com/problem/content/2/
题目描述
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
解答
01背包非常经典,万物起源 。现有容量为 V 的背包,对第 x 个物品有两种选择,即
- 选它,问题转换为在前 x-1 个物品中,用容量 V-weight[x] 进行选择
- 不选它,问题转换为在前 x-1 个物品中,用容量 V 进行选择
我们结果取最大即可。。。这里使用记忆化搜索,边界条件是容量用完,或者物品用完:
#include <bits/stdc++.h>
using namespace std;
int n, v, dp[1009][1009];
vector<int> volumn, weight, amont;
int ms(int x, int V)
{
// 边界
if(x<0 || V<=0) return 0;
// 记忆化
if(dp[x][V]!=-1) return dp[x][V];
// 递推
int ans1=0, ans2=0;
ans1 = ms(x-1, V); // 不选
if(V-volumn[x]>=0) // 选
ans2 = ms(x-1, V-volumn[x]) + weight[x];
int ans = max(ans1, ans2); // 取最大
dp[x][V] = ans;
return ans;
}
int main()
{
memset(dp, -1, sizeof(dp));
cin>>n>>v;
for(int i=0; i<n; i++)
{
int vi, wi; cin>>vi>>wi;
volumn.push_back(vi);
weight.push_back(wi);
}
cout<<ms(n-1, v)<<endl;
return 0;
}
完全背包
原题链接:https://www.acwing.com/problem/content/3/
题目描述
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10
解答
和 01 背包类似,只是我们可以选多次相同物品,我们只需要在 01 背包的基础上,修改转移方程即可:
如图,因为体积从小到大枚举(递归顺序保证我们总是优先解决体积小的背包),保证不会遗漏状态。
因为体积不断变大,能够进行的选择只会越来越多。
代码
#include <bits/stdc++.h>
using namespace std;
int n, v, dp[1009][1009];
vector<int> volumn, weight, amont;
int ms(int x, int V)
{
// 边界
if(x<0 || V<=0) return 0;
// 记忆化
if(dp[x][V]!=-1) return dp[x][V];
// 递推
int ans1=0, ans2=0;
ans1 = ms(x-1, V); // 不选
if(V-volumn[x]>=0) // 选
ans2 = ms(x, V-volumn[x]) + weight[x];
int ans = max(ans1, ans2);
dp[x][V] = ans;
return ans;
}
int main()
{
memset(dp, -1, sizeof(dp));
cin>>n>>v;
for(int i=0; i<n; i++)
{
int vi, wi; cin>>vi>>wi;
volumn.push_back