主要算法

背包问题是贪心和动态规划的经典问题,这里学习一下部分背包问题的解法。 

部分背包问题常用贪心解决,其贪心策略为优先选取单位重量价值最大的商品(介绍完算法再来看一下为什么),下面先看一下主要算法。

 这个算法也比较简单,优先选择单位重量价值的商品,用x【】数组记录是把该商品全部拿走还是只取部分即可。

至于Sort函数,需要自己写,后面的代码有,可以简单看一下。

算法实例应用

 

 以上问题笔者经过计算,结果分别为:

(1)选取商品为:D + F + B + E + 35/60*C 

重量:50 +10 +30 +25 +35 = 150

价值:50 + 40 + 40 +30 +30 + 17.5 = 167.5

(2)选取商品为:F +G + B + A +E +10/50*D

重量:10 + 25 +30 + 35 +40 +10 = 150

价值:40 + 30 +40 +10 + 35 +10 = 165

(3)选取商品为:F + B + G + D + 35/40*E

重量:10 + 30 + 25 +50 +35 = 150

价值: 40 +40 +30 + 50 +30.625 = 190.625

显然,以上三种贪心策略中,每次选取单位重量价值最大的物品装入背包可得到最大价值!

(为了证明算法的正确性,还必须证明背包问题具有贪心选择性质。)

部分背包问题的贪心策略的正确性证明

贪心策略是:总是优先选择单位重量下价值最大的物品

正确性证明 是:使用该贪心策略,可以获得最优解。在这里,最优解就是带走的物品价值最大。

证明思路:先考察一个全局最优解,然后对该解加以修改(一般是采用“剪枝”技巧),使其采用贪心选择,这个选择将原问题变成一个相似的、但是更小的问题。

先假设 物品集合S={W1,W2....Wn}已经按 单位重量价值从小到大排好序了。

并假设 一个全局最优解是:S(i)={Wi1,Wi2,.....Win}。Wi1,Wi2,.....Win是有序的。对于贪心选择而言,总是会优先 选择 Wn 的物品,当Wn 没有后,再选择Wn-1 .....

如果Win = Wn 问题已经得证。因为,我们的最优解S(i)中,已经包含了贪心选择。只要继续归纳下去,Wi(n-1) 就是 Wn-1 ....

如果Win != Wn 运用剪枝技巧,剪掉Win 并 贴上 Wn  此时,得到的是一个更优的解(因为价值更大了 ,Wn > Win)。因为,Wn 是单位重量价值最高的那个物品啊,我们的贪心选择应该选择它,但是这里的最优解S(i)却没有选择它,于是我们用剪枝技巧,将它加入到S(i)中去,并把S(i)中的Win除去。  

这就证明了,如果用贪心策略来进行选择,得到的是最优解。从而证明了贪心算法的正确性。

其实,也就是证明了一定存在一个最优解,这个最优解就是由贪心选择组成的。

代码的实现

ok,废话bb一大堆,上代码让大家瞧瞧 

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
float z[maxn];

void Sort(int n,float w[],float v[]){
	for(int i=0;i<n;i++)
		z[i]=v[i]/w[i];//用z[]存商品的单位重量价值 
	for(int i=0;i<n;i++){//此排序的策略是每次把 单位重量商品的价值最大 的商品放在前面 
		for(int j=i+1;j<n;j++){
			if(z[i]<z[j]){
				float temp = z[i];
				z[i] = z[j];
				z[j]=temp;
				float tempw = w[i];//这里不要忘记把商品的重量和价值同时放到最前面 
				w[i] = w[j];
				w[j] = tempw;
				float tempv = v[i];
				v[i] = v[j];
				v[j] = tempv; 
			}
		}
	}
}

void fire(int n,float w[],float v[],float x[],float carrymax){
	Sort(n,w,v);//根据单位重量商品的价值对商品进行排序 
	int i;
	for(i=0;i<n;i++){
		if(w[i]>carrymax)	break;
		x[i] = 1;	//x[]数组用来记录此次是否选择商品,1代表全部拿走,0代表不拿,小数代表部分拿 
		carrymax -= w[i];
	}
	if(i<=n-1)  x[i] = carrymax/w[i]; 
} 


int main(){
	int n;
	float carry=0; 
	float carrymax,v[maxn],w[maxn],x[maxn];//w[],每个商品的重量,v[]代表每个商品的价值,carrymax代表最大容量 
	memset(x,0,sizeof(x));
	cout<<"请输入最大容量:";
	cin>>carrymax;
	cout<<"请输入商品数量:";
	cin>>n;
	cout<<"请输入每个商品的重量和价值:"<<endl;
	for(int i=0;i<n;i++){
		cin>>w[i]>>v[i];
	}
	fire(n,w,v,x,carrymax);
	for(int i=0;i<n;i++){
		if(x[i]==1){
			cout<<"商品重量为"<<w[i]<<" 价值为"<<v[i]<<"的商品全部拿走"<<endl;
			carry+=v[i];
		}
			
		else if(x[i]==0)
			cout<<"商品重量为"<<w[i]<<" 价值为"<<v[i]<<"的商品不拿走"<<endl;
		else{
			cout<<"商品重量为"<<w[i]<<" 价值为"<<v[i]<<"的商品拿走"<<x[i]<<endl;
			carry+=v[i]*x[i];
		}
	}
	cout<<"最终收获的商品价值为:"<<carry<<endl;
	return 0;
}

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐