注~~~~意
前方
高能
高能
高能
今天我帮大家搜集编写了一些背包问题的板子,若码风不正,请见谅。
1.最基础的01背包(毫无优化版)
思路:
枚举选i个物体,j的体积,so dp[i][j]表示选i个物体,j的体积时最大的收益
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;//n表示物体数量,m表示袋子大小
int w[1010],v[1010],dp[1010][1010];//w:收益 v:体积
int main(){
cin>>n>>m;
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++){//从0开始,一个也不选时体积为0
if(j<v[i]) dp[i][j]=dp[i-1][j];//状态转移方程(选不了)
else dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);//(选或不选)
}
}
cout<<dp[n][m];
return 0;
}
2.01背包优化(1.0版 滚动数组)
思路:
没啥好说的,根据dp的无后效性,第i行只与第i-1行有关,所以我们只需开两个数组,交替使用
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int w[1010],v[1010],f[1010],g[1010];
int main(){
cin>>n>>m;
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++){
if(j<v[i]) g[j]=f[j];
else g[j]=max(f[j],f[j-v[i]]+w[i]);
}
memcpy(f,g,sizeof(g));//复制粘贴数组,滚动数组的核心
}
cout<<f[m];
return 0;
}
3.01背包终极版本(2.0 代码长度优化+滚动数组2次优化)
思路:
分析:如果j的循环从m到v[i]倒着遍历,既可以解决无后效性,有可以节省空间
so
上代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int w[1010],v[1010],f[1010];
int main(){
cin>>n>>m;
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;
}
看完了是不是被吓了一跳,嘻嘻嘻
4.完全背包(终于不是01背包了 )
思路:
这里膜拜一下大佬的解释:完全背包问题
从中我们可以了解到,01背包和他的区别只有一个,真相永远只有一个
j从v[i]到m了!!!
代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int w[1010],v[1010],f[1010];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=v[i];j<=m;j++)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m];
return 0;
}
5.多重背包(朴素版)
思路:
将l个v、w看做相互独立的l个v、w
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int w[2010],v[2010],f[2010],l[2010];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>l[i];
for(int i=1;i<=n;i++)
for(int k=1;k<=l[i];k++)//似乎多了这一行
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
cout<<f[m];
return 0;
}
6.多重背包(二进制优化版)
思路:
分析:用[1,2n]可以通过加法得到[1,2n+1-1]之中的任何值
结论:利用上述二进制优化,可以将时间复杂度压缩至O(n2log2n)
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int w[2010],v[2010],f[2010],l[2010];
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>l[i];
for(int i=1;i<=n;i++){
int res=l[i];
for(int k=1;k<=res;res-=k,k*=2) //二进制优化核心,将l[i]分成二进制的若干组
for(int j=m;j>=v[i]*k;j--)
f[j]=max(f[j],f[j-v[i]*k]+w[i]*k);
for(int j=m;j>=v[i]*res;j--){
f[j]=max(f[j],f[j-v[i]*res]+w[i]*res);
}
}
cout<<f[m];
return 0;
}
7.多重背包(单调队列优化)
引入
先来看一下这道题目
题面
有n个生物,第i个生物会在第i到第ai(i≤ai≤n)天出现,它的攻击力为bi。其中对于所有i(1≤i<n),满足ai≤ai+1。请输出每天出现的生物的攻击力的最大值。
输入格式
第一行一个整数n。接下来n行,每行两个整数ai,bi。
输出格式
一共n行,每行一个数表示答案。第i个整数表示第i天出现的生物的攻击力的最大值。
样例输入
5
3 8
4 9
5 1
5 6
5 1
样例输出
8
9
9
9
6
附上代码
#include<bits/stdc++.h>
using namespace std;
int n;
int v[100010],w[100010];
int q[100010];//数组模拟队列
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]);
int front=1,rear=0;
for(int i=1;i<=n;i++){
for(;rear>=front && w[i]>=w[q[rear]];rear--);
q[++rear]=i;
printf("%d\n",w[q[front]]);
for(;rear>=front && v[q[front]]==i;front++);
}
return 0;
}
多重背包也是运用了这样的思想
一本通1269:【例9.13】庆功会
AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int v,w,t;
int dp[100001];
int c[100001][2];
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
cin>>v>>w>>t;
for(int j=0;j<v;j++){
int front=1,rear=0;
for(int p=j,x=1;p<=m;p+=v,x++){
int e=dp[p]-x*w,r=x+t;
for(;front<=rear && c[rear][0]<=e;rear--);
c[++rear][0]=e,c[rear][1]=r;
dp[p]=c[front][0]+x*w;
for(;front<=rear && c[front][1]==x;front++);
}
}
}
cout<<dp[m];
return 0;
}
8.分组背包
一本通1272:【例9.16】分组背包
我们只需多加上一个组别的维度
AC代码
#include<bits/stdc++.h>
using namespace std;
int n,m,t;
int v[10010],w[10010],a[100010],dp[10010][10010];
vector<int>c[10010];
int main(){
cin>>m>>n>>t;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i]>>a[i];
c[a[i]].push_back(i);
}
for(int i=1;i<=10;i++)
for(int j=0;j<=m;j++){
dp[i][j]=dp[i-1][j];
for(auto k:c[i])
if(v[k]<=j) dp[i][j]=max(dp[i][j],dp[i-1][j-v[k]]+w[k]);
}
cout<<dp[10][m];
return 0;
}
9.二维背包
思路
记住,n维背包只是加了几个维度而已
洛谷P1507 NASA的食物计划
AC代码
#include<bits/stdc++.h>
using namespace std;
int h,t;
int n;
int w[600],s[600],v[600];
int dp[600][600];
int main(){
scanf("%d%d%d",&h,&t,&n);
for(int i=1;i<=n;i++) cin>>w[i]>>s[i]>>v[i];
for(int i=1;i<=n;i++)
for(int j=h;j>=w[i];j--)
for(int k=t;k>=s[i];k--) dp[j][k]=max(dp[j][k],dp[j-w[i]][k-s[i]]+v[i]);
cout<<dp[h][t];
return 0;
}
10.混合背包(补充)
几种背包分类讨论
代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int dp[2010],v[20010],w[20010],s[20010],vi,wi,si,k;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>vi>>wi>>si;
int t=1;
if(si>0){
while(t<=si){
k++;
v[k]=t*vi;
w[k]=t*wi;
s[k]=-1;
si-=t;
t*=2;
}
if(si>0){
k++;
v[k]=si*vi;
w[k]=si*wi;
s[k]=-1;
}
}else{
k++;
v[k]=vi;
w[k]=wi;
s[k]=si;
}
}
for(int i=1;i<=k;i++){
if(s[i]==-1){
for(int j=m;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}else{
for(int j=v[i];j<=m;j++){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
}
cout<<dp[m];
return 0;
}
好啦,今天的分享就到这里啦,感谢大家
哦对了,如果喜欢的话记得点赞收藏加关注