蒟蒻の背包dp学习总结
0/1背包
定义
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。
模板
#include<bits/stdc++.h>
using namespace std;
int n,m;//n表示物品数量,m表示总容积
struct node{
int w,v;//w表示体积,v表示价值
}a[10010];
int dp[30010];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].w>>a[i].v;
}
for(int i=1;i<=n;i++){
for(int j=m;j>=a[i].w;j--){
dp[j]=max(dp[j],dp[j-a[i].w]+a[i].v);
}
}
cout<<dp[m];
return 0;
}
例题
P1048 采药
思路
由题意可得,每株草药只可采一次,且给定了总背包容量和每株草药的信息,则可判定该题是0/1背包问题
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
int t,m;
struct node{
int time,value;
}a[110];
int dp[1010];
int main(){
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>a[i].time>>a[i].value;
}
for(int i=1;i<=m;i++){
for(int j=t;j>=a[i].time;j--){
dp[j]=max(dp[j],dp[j-a[i].time]+a[i].value);
}
}
cout<<dp[t];
return 0;
}
P1049 装箱问题
思路
该题仅给定了每件物品的体积和背包总容量,但是题目中隐晦地告诉我们:价值就是每件物品的体积! ,故此题只需使用0/1背包模板求所占的最大体积,最后输出总体积-求所占的最大体积即可,是一道0/1背包基础变形题
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
int n;
int a[40];
int v;
int dp[20010];
int main(){
cin>>v>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
for(int j=v;j>=a[i];j--){
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
}
}
cout<<v-dp[v];
return 0;
}
P1060 开心的金明
思路
由题意可得,该题的**每件价值为体积(每件物品所花钱数)* 重要度 **,那么这题就变成了常规的0/1问题,故此题只需仔细读题即可求出答案
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
int n,m;
struct node{
int w,v;
}a[30];
int dp[30010];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i].w>>a[i].v;
a[i].v*=a[i].w;
}
for(int i=1;i<=m;i++){
for(int j=n;j>=a[i].w;j--){
dp[j]=max(dp[j],dp[j-a[i].w]+a[i].v);
}
}
cout<<dp[n];
return 0;
}
完全背包
定义
有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
模板
#include<bits/stdc++.h>
using namespace std;
int n,m;//n表示物品数量,m表示总容积
struct node{
int w,v;//w表示体积,v表示价值
}a[10010];
int dp[30010];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i].w>>a[i].v;
}
for(int i=1;i<=n;i++){
for(int j=a[i].w;j<=m;j++){
dp[j]=max(dp[j],dp[j-a[i].w]+a[i].v);
}
}
cout<<dp[m];
return 0;
}
例题
P1616 疯狂的采药
思路
由题意可得,每株草药可采无限次,也给定了每株草药的信息和总容积,则该题为一道完全背包题
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
int t,m;
struct node{
int v,w;
}a[10010];
int dp[10000010];
int main(){
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>a[i].w>>a[i].v;
}
for(int i=1;i<=m;i++){
for(int j=a[i].w;j<=t;j++){
dp[j]=max(dp[j],dp[j-a[i].w]+a[i].v);
}
}
cout<<dp[t];
return 0;
}
P5662 纪念品
思路
**这题题面有一句关键的话,“当日购买的纪念品也可以当日卖出换回金币”!**这句话可以帮我们简化状态,因为如果一个纪念品,你想连续持有若干天,可以看做第一天买,第二天早上立刻卖掉,然后第二天买回来,第三天早上立刻卖掉,然后第三天买回来……所以我们就不需要记录每天手里持有多少纪念品了,统一认为我们今天买的纪念品,明天早上就立刻卖掉。明天又是新的一天,用所有的现金,进行新的决策就好了。
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
int t,n,m;
int a[110][110];
int dp[10100];
int main(){
cin>>t>>n>>m;
for(int i=1;i<=t;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=t;i++){
memset(dp,0,sizeof(dp));
for(int j=1;j<=n;j++)
for(int k=a[i][j];k<=m;k++)
dp[k]=max(dp[k],dp[k-a[i][j]]+a[i+1][j]-a[i][j]);
m=max(dp[m]+m,m);
}
cout<<m;
return 0;
}
多重背包
定义
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
模板
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[110],w[110],s[110];
int dp[110];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>s[i];
}
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
for(int k=1;k<=s[i]&&k*v[i]<=j;k++){
dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[m];
return 0;
}