动态规划 - 背包详解(下)
题目链接:
1269:【例9.13】庆功会
哈喽,大家好,我们继续来讲背包问题。
上期博客:动态规划 - 背包详解(中)
先复习一下前几期博客的内容。
01 背包:
一维:
//01背包
#include<bits/stdc++.h>
using namespace std;
//定义需要的数组和变量
const int N=25;
int n,m,w[N],v[N],f[1010];int main(){//输入 cin>>m>>n;for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; for(int i=1;i<=n;i++){for(int j=m;j>=w[i];j--){//状态转移方程f[j]=max(f[j],f[j-w[i]]+v[i]);}}//输出最大值 (max) 并结束程序 cout<<f[m];return 0;
}
二维:
#include<bits/stdc++.h>
using namespace std;
//定义
const long long N=1010;
long long n,m,dp[N][N],w[N],v[N];
// n:物体数量
// m:背包最大容量
// dp[i][j]:用来存储 在前 i 件物品时,背包容量为 j 时的最大价值
// w[i]:存储第 i 件物体的质量
// v[i]:存储第 i 件物体的价值 int main(){//输入 cin>>n>>m;for(int i=1;i<=n;i++) cin>>w[i]>>v[i];for(int i=1;i<=n;i++){//对前 i 件物品的遍历 for(int j=m;j>0;j--){//容量从大到小遍历 //状态转移方程 //在第 i 件物品能放得下时,考虑在放和不放中的最大价值方案 if(j-w[i]>=0) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//否则在第 i 件物品放不下时,此时不放第 i 件物品(即与前 i-1 件物品的最大价值相吻合) else dp[i][j]=dp[i-1][j];}}//输出并结束程序 cout<<dp[n][m];return 0;
}
还记得完全背包问题吗?代码如下:
#include<bits/stdc++.h>
using namespace std;
//定义
const long long N=1010;
long long n,m,dp[N][N],w[N],v[N];
// n:物体数量
// m:背包最大容量
// dp[i][j]:用来存储 在前 i 件物品时,背包容量为 j 时的最大价值
// w[i]:存储第 i 件物体的质量
// v[i]:存储第 i 件物体的价值 int main(){//输入 cin>>n>>m;for(int i=1;i<=n;i++) cin>>w[i]>>v[i];for(int i=1;i<=n;i++){//对前 i 件物品的遍历 for(int j=m;j>0;j--){//容量从大到小遍历 //状态转移方程 //在第 i 件物品能放得下时,考虑在放和不放中的最大价值方案 if(j-w[i]>=0) dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//否则在第 i 件物品放不下时,此时不放第 i 件物品(即与前 i-1 件物品的最大价值相吻合) else dp[i][j]=dp[i-1][j];}}//输出并结束程序 cout<<dp[n][m];return 0;
}
还有分组背包:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int n,m,t,x,y,z,dp[N];
vector<int>a[N],b[N];
signed main(){cin>>m>>n>>t;for(int i=0;i<n;i++){cin>>x>>y>>z;a[z].push_back(x);b[z].push_back(y);}for(int i=1;i<=t;i++){for(int j=m;j>=0;j--){for(int k=0;k<a[i].size();k++){if(j<a[i][k]){}else{dp[j]=max(dp[j-a[i][k]]+b[i][k],dp[j]);}}}}cout<<dp[m];return 0;
}
于是我们就复习完了。
现在进入正题。
我们今天来讲多重背包。
先看题:
【题目描述】
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。
【输入】
第一行二个数n(n≤500),m(m≤6000),其中n代表希望购买的奖品的种数,m表示拨款金额。
接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可),其中v≤100,w≤1000,s≤10。
【输出】
一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。
【输入样例】
5 1000 80 20 4 40 50 9 30 50 7 40 30 6 20 20 1
【输出样例】
1040
看到此题,其实有两种方法(时间复杂度差不多,空间第一种略大):
第一种:转换成 01 背包。
我们可以将输入的每种若干数量的物品拆成一些物品,就像把
80 20 4
拆成
80 20
80 20
80 20
80 20
于是,就可以用 01 背包啦:
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int n,m,x,nn=1,y,z,w[N],v[N],f[N*4];
int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>x>>y>>z;for(int i=1;i<=z;i++){w[nn]=x;v[nn]=y;nn++;}}for(int i=1;i<=nn;i++){for(int j=m;j>=w[i];j--){f[j]=max(f[j],f[j-w[i]]+v[i]);}}cout<<f[m];return 0;
}
第二种:正正宗宗的多重背包
其实也不难,代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=6010;
int n,m,w[N],v[N],s[N],f[N];
signed main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>w[i]>>v[i]>>s[i];}for(int i=1;i<=n;i++){for(int j=m;j>=w[i];j--){for(int k=1;k<=s[i]&&k*w[i]<=j;k++){f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);}}}cout<<f[m];return 0;
}
注意:价格和价值是不一样的。
然后大家不要像我第一遍看错大小,否则……像下面:
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int n,m,w[N],v[N],s[N],f[N];
int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>w[i]>>v[i]>>s[i];}for(int i=1;i<=n;i++){for(int j=m;j>=w[i];j--){for(int k=1;k<=s[i]&&k*w[i]<=j;k++){f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);}}}cout<<f[m];return 0;
}
于是只有 90 分了。
最后,这题就讲完了,谢谢大家,我们下次再见!