动态规划之背包大全

注~~~~意

前方

高能

高能

高能

今天我帮大家搜集编写了一些背包问题的板子,若码风不正,请见谅。

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.混合背包(补充)

acwing混合背包

几种背包分类讨论

代码

#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;
}

好啦,今天的分享就到这里啦,感谢大家

哦对了,如果喜欢的话记得点赞收藏加关注

Bye~Bye

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值