背包再学习笔记

背包再学习

之前学习的几个背包都是背几个一维数组的板子,没有深入的理解其中的含义,当碰到一个相似的背包的问题时,板子出现了短板,这时就难于写出题目;

先说 01 背包:

一维数组的板子大家都会,这里讲二维:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);

这里的 i 表示第几个物品,j表示目前用的背包体积;

还有一个要注意的是背包不只是求max,min照样可以,只是初始化问题,我前面的一篇博客有讲到;

伪代码:

memset(dp,0,sizeof(0));//一般无特殊情况,初始为0 
	for(int i=1;i<=n;i++){//表示有 n 个物品 
		for(int j=0;j<=s;j++){//表示背包体积为 s 
			dp[i][j]=dp[i-1][j];//表示没有取 i 物品
			if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);//取了 i 物品 
 		}
	}

再谈完全背包:

学习博客

完全背包和01背包的区别在于一个物品可以取无数个,直到背包装不下;

我们可以很显然的从 01 背包推出表达式:

dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]*k]+w[i]*k) (0<=k * v[i]<=j)

伪代码:

	memset(dp,0,sizeof(0));//一般无特殊情况,初始为0 
	for(int i=1;i<=n;i++){//表示有 n 个物品 
		for(int j=0;j<=s;j++){//表示背包体积为 s 
			int m=j/v[i];//最多能拿几个物品 i  
			for(int k=0;k<=m;k++){
				dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
			} 
 		}
	}

虽然时间和空间复杂度都很大,但是对于我们深刻理解背包,理解动态规划都有很大的意义;

多重背包

这个跟01背包的区别就是物品的个数有限,它的解法和完全背包非常相似,物品数量由在背包容量允许的情况下最多能拿几个变成了物品有几个而已;

这里给出的伪代码用一维表示:

for(int i=1;i<=n;i++){
		for(int j=V;j>=0;j--){
			for(int k=1;k<=s[i];k++){//s[]数组为该物品的数量
				if(j>=k*v[i]) dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);		
			}
		}
	}

这种多重背包解法的复杂度为O(n^3),一般的题目是过不了的;

一种优化方法,就是把每个物品的数量用二进制拆分,这个方法十分巧妙,拆完之后相当于多个物品当做一个物品;

代码:

#include
#define LL long long
#define pa pair
#define lson k<<1
#define rson k<<1|1
#define inf 0x3f3f3f3f
//ios::sync_with_stdio(false);
using namespace std;
const int N=200100;
const int M=4001000;
const LL mod=998244353;
int v,w,s;
int dp[2100];
struct Node{
	int v,w;//物品的体积和价值 
};
vector<Node>goods;//物品
int main(){
	ios::sync_with_stdio(false);
	int n,V;
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		cin>>v>>w>>s;
		for(int j=1;j<=s;j*=2){
			s-=j;
			goods.push_back({j*v,j*w});
		}
		if(s>0) goods.push_back({v*s,w*s});
	}
	for(int i=0;i<goods.size();i++){
		for(int j=V;j>=0;j--){
			if(j>=goods[i].v) dp[j]=max(dp[j],dp[j-goods[i].v]+goods[i].w);
		}
	}
	cout<<dp[V]<<endl;
	return 0;
}

分组背包

分组背包和多重背包的区别在于,多重背包枚举第 i 种物品选几个,分组背包枚举第 i 组物品选哪个?

模板题:

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。


代码:

#include
#define LL long long
#define pa pair
#define lson k<<1
#define rson k<<1|1
#define inf 0x3f3f3f3f
//ios::sync_with_stdio(false);
using namespace std;
const int N=200100;
const int M=4001000;
const LL mod=998244353;
int v[110],w[110],dp[110];
int main(){
	ios::sync_with_stdio(false);
	int n,V;
	cin>>n>>V;
	for(int i=1;i<=n;i++){
		int s;
		cin>>s;
		for(int j=1;j<=s;j++) cin>>v[j]>>w[j];
		for(int j=V;j>=0;j--){
			for(int k=1;k<=s;k++){//第 i 组第几个物品 
				if(j>=v[k]) dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
			}
		}
	}
	cout<<dp[V]<<endl;
	return 0;
}

你可能感兴趣的:(#,背包,背包)