算法学习 【第二周】贪心算法实例 Ⅱ

简介: 背包问题解析

一、前言

在前面一期博客中,我们解析了贪心算法实例中的最优装载问题,本期博客我们紧接着来解析贪心算法中另一个经典的背包问题。

二、背包问题

有一天,阿里巴巴赶着一头毛驴上山砍柴。砍好柴准备下山时,远处突然出现一股烟尘,弥漫着向上空飞扬,朝他这儿卷过来,而且越来越近。阿里巴巴心里害怕,担心碰到的是一伙儿强盗,他赶紧把毛驴赶到丛林的小道里,自己爬到一棵大树上躲了起来,这棵大树生长在一个大石头旁边。靠近以后,他才看清原来是一支马队,他们共有四十人,一个个年轻力壮、行动敏捷。一个首领模样的人背负沉重的鞍袋,他从丛林中来到那个大石头跟前,喃喃地说道:“芝麻,开门吧! "随着那个头目的喊声,大石头前突然出现一道宽阔的门路,于是强盗们鱼贯而入。阿里巴巴躲在树上观察他们,直到他们走得无影无踪之后,才从树上下来。他大声喊道:“芝麻,开门吧! ”他的喊声刚落,洞门立刻打开了。他小心翼翼地走了进去,一下子惊呆了,洞中堆满了财物,还有多得无法计数的金银珠宝,有的散堆在地上,有的盛在皮袋中。突然看见这么多的金银财宝,阿里巴巴深信这肯定是强盗们数代经营、掠夺所积累起来的宝窟。为了让乡亲们开开眼界,见识一下这些宝物,他想一种宝物只拿一个,如果太重就用锤子凿开,但毛驴的运载能力是有限的,怎么才能用毛驴运走价值最大的财宝分给乡亲们呢?

1、问题分析

这是书中的原作者给我们的设置的具体场景的背包问题,我们简化一下问题就是有n种物品,每种物品只有一个,第种物品的重量为wi,价值为vi,背包的容量为W,物品可以分割。如何放置物品,使装入背包的物品价值之和最大?

我们假定所有物品的重量和价格都是非负的。背包所能承受的最大重量为W。如果物品可分割,则问题称为背包问题,如果限定每种物品只能选择0个或1个,则问题称为0-1背包问题

我们可以用公式表示为:

image-20221029163147711.png

如果限定物品j最多只能选择bj个,则问题称为有界背包问题

我们可以用公式表示为:

image-20221029163259517.png

如果不限定每种物品的数量,则问题称为无界背包问题

各类复杂的背包问题总可以变换为简单的0-1背包问题进行求解。

我们回到上面的题目,如果我们要使装入背包的物品价值之和最大,按照贪心策略的话我们应该每次选择单位重量价值最大的物品装入背包。

2、算法设计

  • 确定合适的数据结构并初始化,我们首先将物品的重量、价值和单位重量价值定义为一种结构体类型,然后对物品按单位重量价值从大到小进行排序。
  • 然后根据贪心策略,我们按照单位重量价值从大到小选取物品,直到达到背包的最大容量,如果在装入第i个物品时超出背包容量,则取该物品的一部分装入背包。

3、算法解析

我们在这里假设背包容量W=30,然后列出物品的价值和重量表:

物品i 1 2 3 4 5 6 7 8 9 10
重量w[i] 4 2 9 5 5 8 5 4 5 5
价值v[i] 3 8 18 6 8 20 5 6 7 15

我们现在按照贪心策略每次选单位重量价值(价值/重量)大的物品,因此我们可以按单位重量价值对物品进行降序排序,排序后结果如下:

物品i 2 10 6 3 5 8 9 4 7 1
重量w[i] 2 5 8 9 5 4 5 5 5 4
价值v[i] 8 15 20 18 8 6 7 6 5 3
单位重量价值p[i] 4 3 2.5 2 1.6 1.5 1.4 1.2 1 0.75

然后我们按照安心策略每次选择单位重量价值最大的物品装入背包。

物品ID 背包剩余容量 当前已装入物品的最大价值
2 30-2=28 8
10 28-5=23 8+15=23
6 23-8=15 23+20=43
3 15-9=6 43+18=61
5 6-5=1 61+8=69
8 剩余容量为1,8号物品重量为4,无法全部装入 69+1×1.5=70.5

我们按照上面表格依次装入物品,到第八装入的时候背包就被装满了。

由上述表格可知,这个问题的最优解就是物品ID组成的(2,10,6,3,5,8),其中最后一个物品是部分装入,只装入了1/4,最后装入物品的最大价值是70.5。

4、算法详解

根据之前的分析结果,我们首先定义一个结构体:

structnode{
doublew;   //每种物品的重量doublev;   //每种物品的价值doublep;   //每种物品的耽误重量价值}

然后我们对物品按单位重量价值进行排序,我们仍然利用之前讲过的排序函数sort最物品按单位重量价值从大到小进行排序,在对结构体类型的数据进行排序时,如果没有自定义比较函数,sort函数将不知道按照结构体的哪个成员进行排序,因此我们自定义比较函数cmp,指定按照物品的单位重量价值进行降序排列:

boolcmp(nodea,nodeb){    //自定义比较函数cmpreturna.p>b.p;         //指定按照物品的单位重量价值进行降序排序}
sort(s,s+n,cmp);            //前两个参数分别为待排序数组的首地址和尾地址,cmp为比较函数

然后我们使用贪心算法求解问题,在单位重量价值排序的基础上,如果当前物品的重量小于或等于剩余容量,则可以装入,将剩余容量减去当前物品的重量,同时将已装入物品的价值加上当前物品的价值。如果当前物品的重量大于剩余容量,则表示不可以全部装入,但可以部分装入,直到背包装满,将剩余容量乘以当前物品的单位重量价值,与已装入物品的价值相加,即为已装入物晶的最大价值。

doublesolve(intn,doubleW){
doublesum=0.0;             //sum表示已装入物品的价值之和doublecleft=W;             //背包的剩余容量for(inti=0;i<n;i++){       //使用贪心算法求解问题if(s[i].w<=cleft){      //如果物品的重量小于或等于剩余容量cleft-=s[i].w;
sum+=s[i].v;
        }
else{                   //如果物品的重量大于剩余容量sum+=cleft*s[i].p;  //部分装入break;
        }
    }
returnsum;
}

5、算法分析

  • 时间复杂度:时间主要耗费在对物品按单位重量价值进行排序上,一般采用快速排序法,时间复杂度是O(nlogn)。
  • 空间复杂度:空间主要耗费在存储物品的单位重量价值上,空间复杂度是O(n)。

6、整体代码

以下代码是随书附带的源码,大家可以看看:

//program 2.3 背包问题 #include<iostream>#include<algorithm>usingnamespacestd;
constintM=10005;
structnode{
doublew;//每个物品的重量doublev;//每个物品的价值doublep;//性价比}s[M];
boolcmp(nodea,nodeb){//自定义比较函数 returna.p>b.p;//根据物品的单位价值从大到小排序}
doublesolve(intn,doubleW){
doublesum=0.0;//sum表示示装入物品的价值之和doublecleft=W;//背包剩余容量 for(inti=0;i<n;i++){//贪心算法求解 if(s[i].w<=cleft){//如果物品的重量小于等于剩余容量 cleft-=s[i].w;
sum+=s[i].v;
        }
else{//如果物品的重量大于剩余容量 sum+=cleft*s[i].p;//部分装入break;
        }
    }
returnsum; 
}
intmain(){
intt,n;//t为测试用例个数,n为物品个数doubleW;//背包容量 cin>>t;
while(t--){
cin>>n>>W;
for(inti=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);
cout<<solve(n,W)<<endl;//输出装入宝物的最大价值    }
return0;
}

三、最后我想说

本期博客就到这里结束了,目前举例了两个贪心算法实例,还有很多实例,我后续也会进行更新了,这个14天阅读挑战赛我应该只能更新五篇博客了,不过算法这方面我肯定不会断更的,因为算法很重要,必须要好好的提升自己这方面的知识。

谢谢大家!

目录
相关文章
|
11天前
|
机器学习/深度学习 人工智能 资源调度
【博士每天一篇文献-算法】连续学习算法之HAT: Overcoming catastrophic forgetting with hard attention to the task
本文介绍了一种名为Hard Attention to the Task (HAT)的连续学习算法,通过学习几乎二值的注意力向量来克服灾难性遗忘问题,同时不影响当前任务的学习,并通过实验验证了其在减少遗忘方面的有效性。
28 12
|
3天前
|
算法 Java
掌握算法学习之字符串经典用法
文章总结了字符串在算法领域的经典用法,特别是通过双指针法来实现字符串的反转操作,并提供了LeetCode上相关题目的Java代码实现,强调了掌握这些技巧对于提升算法思维的重要性。
|
5天前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
11天前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之HNet:Continual learning with hypernetworks
本文提出了一种基于任务条件超网络(Hypernetworks)的持续学习模型,通过超网络生成目标网络权重并结合正则化技术减少灾难性遗忘,实现有效的任务顺序学习与长期记忆保持。
14 4
|
11天前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之RWalk:Riemannian Walk for Incremental Learning Understanding
RWalk算法是一种增量学习框架,通过结合EWC++和修改版的Path Integral算法,并采用不同的采样策略存储先前任务的代表性子集,以量化和平衡遗忘和固执,实现在学习新任务的同时保留旧任务的知识。
49 3
|
11天前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-综述】基于脑启发的连续学习算法有哪些?附思维导图
这篇博客文章总结了连续学习的分类,包括经典方法(重放、正则化和稀疏化方法)和脑启发方法(突触启发、双系统启发、睡眠启发和模块化启发方法),并讨论了它们在解决灾难性遗忘问题上的优势和局限性。
13 2
|
5天前
|
算法
【算法】贪心算法——柠檬水找零
【算法】贪心算法——柠檬水找零
|
5天前
|
算法
【算法】贪心算法简介
【算法】贪心算法简介
|
1月前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
|
1月前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战

热门文章

最新文章