背包问题总结

背包总结

01背包

每种物品要么取(最多取一个),要么不取。

一.求背包能装的最大总价值

	int i,j,v[100]={0},w[100]={0},f[1000]={0},n,m;
	cin>>n>>m;				//n为物品个数,m为最大重量 
	for(i=1;i<=n;i++)
		cin>>v[i]>>w[i];		//v[i]为物品价值,w[i]为物品重量
		
	for(i=1;i<=n;i++){
		for(j=m;j>=w[i];j--){				
			f[j]=MAX(f[j],f[j-w[i]]+v[i]);	//f[j]为当背包重量为i时所取物品总价值 
		}
	}
	cout<<f[m]<<endl;	//f[m]即为当背包重量为m时能取到的最大总价值 

二.求装满背包的方案数

	int i,j,v[100]={0},w[100]={0},f[1000]={0},n,m;
	cin>>n>>m;				//n为物品个数,m为最大重量 
	for(i=1;i<=n;i++)
		cin>>v[i]>>w[i];		//v[i]为物品价值,w[i]为物品重量
	
	f[0]=1;	//每加到一次f[0]都代表装满一次 
	for(i=1;i<=n;i++){
		for(j=m;j>=w[i];j--){
			f[j]=f[j]+f[j-w[i]];	//f[j]为 当前重量的方案数 加上 不取当前物品的方案数 
		}
	}
	cout<<f[m]<<endl;	//f[m]即为装满时的方案数 

完全背包

每种物品不限制取的次数

	int i,j,v[100]={0},w[100]={0},f[1000]={0},n,m;
	cin>>n>>m;
	for(i=1;i<=n;i++)
		cin>>v[i]>>w[i];
		
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++){				//这里用正序的原因是可以重复取
			f[j]=MAX(f[j],f[j-w[i]]+v[i]);	//每次遍历j时,都会把第i个物品在j为各个重量时能取的都取一次  
		}
	}
	cout<<f[m]<<endl;

多重背包:

和完全背包一样,只是每件物品有个数限制

	int i,j,k=0,v[100]={0},w[100]={0},num[100]={0},f[1000]={0},n,m;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
		scanf("%d%d%d",&v[i],&w[i],&num[i]);	//num是该物品的个数 

	for(i=1;i<=n;i++){
		for(j=m;j>0;j--){
			if(j>=w[i]*num[i]){			//如果能取满该物品
				f[j]=MAX(f[j],f[j-num[i]*w[i]]+num[i]*v[i]);
			}
			else{
				k=1;					//不能取满就遍历 取几个时值能最大 
				while(k<num[i] && j>=k*w[i]){
					f[j]=MAX(f[j],f[j-k*w[i]]+k*v[i]);
					k++;
				}
			}
		}
	}
	printf("%d\n",f[m]);
5 100
12 8 2
8 7 3
16 11 1
20 15 1
40 34 2

124

这时候忽然想到了该如何求出每种物品取了多少个呢?但是下面这种方法只求出了一种情况,所有情况的话好像也只有枚举了。。反正我是没想出啥好办法,暂时先这样

#include <stdio.h>
int MAX(int a,int b){
	return a>b?a:b;
}
void Copy(int x[][100],int a,int b){
	int i=1;
	for(i=1;i<=5;i++){
		x[a][i]=x[b][i];
	}
}
int main(){
	int i,j,k=0,t=0,v[100]={0},w[100]={0},num[100]={0},f[1000]={0},n,m,x[1000][100]={0};	//x[m][n]是当容量为m时,每样物品取的个数 
	scanf("%d%d",&n,&m);	//物品种类为n,容量为m
	for(i=1;i<=n;i++)
		scanf("%d%d%d",&v[i],&w[i],&num[i]);	//v是该物品的价值,w是物品的重量,num是该物品的个数 

	for(i=1;i<=n;i++){
		for(j=m;j>0;j--){
			if(j>=w[i]*num[i]){			//如果能取满该物品
				if(f[j]<f[j-num[i]*w[i]]+num[i]*v[i]){
					f[j]=f[j-num[i]*w[i]]+num[i]*v[i];
					Copy(x,j,j-num[i]*w[i]);	//将容量为j-num[i]*w[i]时的方案赋给j时的,然后在此基础上做改变
					x[j][i]=num[i];				//这里要明白,x[j][i]中放的是当容量为j时,所取总值最大的方案数,所以x一直是最优解
				}
			}
			else{
				k=1;					//不能取满就遍历 取几个时值能最大 
				t=f[j];
				while(k<num[i] && j>=k*w[i]){
					f[j]=MAX(f[j],f[j-k*w[i]]+k*v[i]);
					k++;
				}
				if(f[j]!=t){
					Copy(x,j,j-k*w[i]);			//将容量为j-k*w[i]时的方案赋给j时的,然后在此基础上做改变
					x[j][i]=k;
				}
			}
		}
	}
	printf("%d\n",f[m]);
	for(i=1;i<=n;i++){
		printf("%d ",x[m][i]);
	}
	return 0;
}
5 100
12 8 2
8 7 3
16 11 1
20 15 1
40 34 2

124
2 0 0 1 2

总结一下:

刷题也刷了不短时间了,但仍然有一些题看着套路很熟悉却不会做。想来想去,还是记录下来,而不是仅仅留存一个程序,就当是给自己再梳理一遍顺便加深一下印象,优化一下当初的程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值