01背包问题
01背包问题
1.01背包问题介绍
01背包是指有N件物品和一个容量为V的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
01背包中的“01”是指每个物品只能选或不选,也就是选1次和选0次。
2.01背包问题例题
题目描述
一个旅行者有一个最多能装 \(V\) 公斤的背包,现在有 \(N\) 件物品,它们的重量分别是 \(W_1,W_2,...,W_n\),它们的价值分别为\(V_1,V_2,...,V_n\),求旅行者能获得最大总价值。
输入
第一行:两个整数,M(背包容量,\(M<=200\))和 \(N\)(物品数量,\(N<=30\));
第 \(2..N+1\) 行:每行二个整数 \(W_i,V_i\) ,表示每个物品的重量和价值。
输出
仅一行,一个数,表示最大总价值。
输入样例
10 4
2 1
3 3
4 5
7 9
输出样例
12
注:本题来自AcWing题库第2题
3.01背包问题思路
Dp一般分为两个思考部分
第一个是状态表示,也就是 \(f\) 数组。
在01背包问题中,\(f[i][j]\) 代表从前 \(i\) 个物品中选且总重量不超过 \(j\) 的选法集合,它的值是所有算法集合的最大值。
第二个是状态计算,也就是状态转移方程。
前一阶段的终点就是后一阶段的起点,对前一阶段的状态作出某种决策,产生后一阶段的状态,这种关系描述了由k阶段到k+1阶段状态的演变规律,称为状态转移方程。
大致是这个意思,具体可能有错误
01背包问题可以把 \(f[i][j]\) 分成含 \(i\) 和不含 \(i\) 的集合,然后取最大值。所以动态转移方程为:
\(f[i-1][j]\):在这个集合里取不含 \(i\) 的小集合。
\(f[i-1][j-v[i]]+w[i]\):用Y总的话来说就是“曲线救国”。要想直接求含 \(i\) 的集合很难,所以我们可以先将第 \(i\) 个物品的重量减去,再在不含 \(i\) 的集合里找,最后加上 \(i\) 的权重就好了。
注:在集合的划分上一般要做到不重不漏,只有个别情况不用
4.01背包问题代码
#include<iostream>
#define MAXN 1005
using namespace std;
int v[MAXN],w[MAXN],f[MAXN][MAXN];
int main(){
int n,m;
cin>>m>>n;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
cout<<f[n][m];
return 0;
}
5.01背包问题优化
我们通过观察可以得出,\(f\) 数组的第一维只用到了 \(i-1\),所以我们可以用滚动数组存储,优化掉第一维。
状态转移方程:
6.代码(优化)
#include<iostream>
using namespace std;
const int MAXN=1005;
int v[MAXN],w[MAXN],f[MAXN];
int main(){
int n,m;
cin>>m>>n;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m];
return 0;
}
完awa~
如果觉得好就点个赞吧,您的支持就是本蒟蒻最大的动力。