【贪心算法】阿里巴巴与四十大盗——背包问题与0-1背包问题

前言

关于贪心算法,我在这篇博客中已经做了简单的介绍。初识贪心算法

下面来介绍一下贪心算法中的一个经典的问题——背包问题

一、问题描述

一天,阿里巴巴赶着一头毛驴上山砍柴,无意间在远处发现了一群盗贼,他们把偷窃强盗来的宝物全部藏在一个山洞里,山洞前有一个洞门,只见盗贼首领说了一句“芝麻开门”,顷刻间,洞门大开,里面漏出了阿里巴巴这辈子都没有见过的稀世珍宝。阿里巴巴心想:这些盗贼真的可恨,囤积了这么多盗来的宝物藏匿于此,等他们走掉后,不如偷偷把一部风宝物偷出来分给村里的穷人们,让他们开开眼界,他打算一种宝物只拿一个,如果太重就用锤子锤开分解装走一部分,但是毛驴的运载能力有限,怎样才能用自己的驴子运走最大价值的财宝分给那些穷人们呢?

二、问题分析

简而言之,假设山洞中一共有n种宝物,每一种宝物都有一定的重量w与价值v,毛驴的最大运载能力为m,一种宝物只能拿走一样,且宝物可以分割带走。那么怎样才能使毛驴运走的宝物价值最大呢?

这是一个典型的贪心问题,我们可以尝试一下三种贪心策略:

(1)每次挑选价值最大的宝物装入背包

(2)每次挑选重量最小的宝物装入背包

(3)每次挑选性价比最大的宝物装入背包

我们思考发现:如果选择价值最大的宝物优先装,但是它的重量可能很大;如果选择重量最小的宝物优先装,但是它的价值又不一定大。所以,第一种和第二种贪心策略显然不是好的策略,舍弃。第三章贪心策略从性价比的角度出发,即每一件宝物都有各自的性价比,即:单位价值p=w/v 我们采取性价比最大的宝物优先装载,最终装不下的宝物进行分割,等于最后剩余的装载量乘以该宝物的单位价值,最终合成,那么最终的价值一定是最大的,即最优解。

三、背包问题

代码中有涉及到C++sort排序函数降序排列的方法,不太了解的小伙伴可以去看看我另一篇文章

链接:C++中常用的sort排序函数

背包问题的完整代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int N =1000005;
typedef struct treasure{
	double w;
	double v;
	double p;
}treasure;
//自定义比较函数cmp
bool cmp(treasure t1,treasure t2){
	return t2.p<t1.p;
} 
treasure s[N];
int main(){
	int n,m;
	cout<<"请输入宝物的种类个数n和驴子的最大承载重量m"<<endl;
	cin>>n>>m;
	cout<<"请分别输入这"<<n<<"种宝物所对应的重量与价值"<<endl;
	for(int i=0;i<n;i++){
		cin>>s[i].w>>s[i].v;
		s[i].p=s[i].v/s[i].w;
	} 
	sort(s,s+n,cmp);//对宝物以性价比进行降序排列
	double sum=0.0;
	for(int i=0;i<n;i++){
		if(m>s[i].w){
			m-=s[i].w;
			sum+=s[i].v;
		}
		else{
			sum+=m*s[i].p;//分解宝物,部分装入
			break; 
		}
	} 
	cout<<"能够装入宝物的最大价值为:"<<sum<<"亿元"<<endl;
	return 0;
}

四、0-1背包问题

(1)区别

背包问题与0-1背包问题的区别在哪? 区别就在于剩余物品是否可以进行分割

物品可以进行分割的装载问题称为背包问题,物品不可以分割的装载问题称为0-1背包问题。

(2)原因

在物品不可分割的情况下,即0-1背包问题,其已经不具备贪心选择性质,即原问题的最优解无法通过一系列的局部最优解而得到,因此这类问题最终得到的有可能只是近似解。例如上面的背包问题,如果要求物品不可分割,那么最后剩余的装载空间将永远无法填满,永远闲置,故最终可能无法得到一个最优解,而是最优解的近似解。

(3)代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N =1000005;
typedef struct treasure{
	double w;
	double v;
	double p;
}treasure;
//自定义比较函数cmp
bool cmp(treasure t1,treasure t2){
	return t2.p<t1.p;
} 
treasure s[N];
int main(){
	int n,m;
	cout<<"请输入宝物的种类个数n和驴子的最大承载重量m"<<endl;
	cin>>n>>m;
	cout<<"请分别输入这"<<n<<"种宝物所对应的重量与价值"<<endl;
	for(int i=0;i<n;i++){
		cin>>s[i].w>>s[i].v;
		s[i].p=s[i].v/s[i].w;
	} 
	sort(s,s+n,cmp);//对宝物以性价比进行降序排列
	double sum=0.0;
	for(int i=0;i<n;i++){
		if(m>s[i].w){
			m-=s[i].w;
			sum+=s[i].v;
		}
		else{
			//sum+=m*s[i].p;//分解宝物,部分装入
			break; 
		}
	} 
	cout<<"能够装入宝物的最大价值为:"<<sum<<"亿元"<<endl;
	return 0;
}

 最终价值与背包问题相比少了0.6,这一点就是物品不可分割所造成的。 

最后留一个小问题供大家思考:

为什么在加勒比海盗船——最优装载问题中,船在没有装满还留有剩余空间的情况下,最终所得到的仍然是最优解。而在0-1背包问题中,在没有装满的情况下,最终得到的有可能只是最优解的近似解?

一键三连,动力不竭!

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aricl.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值