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

简介: 背包问题解析

一、前言

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

二、背包问题

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

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天阅读挑战赛我应该只能更新五篇博客了,不过算法这方面我肯定不会断更的,因为算法很重要,必须要好好的提升自己这方面的知识。

谢谢大家!

北天
+关注
目录
打赏
0
0
0
0
300
分享
相关文章
为什么要学习数据结构与算法
今天,我向大家介绍一门非常重要的课程——《数据结构与算法》。这门课不仅是计算机学科的核心,更是每一位开发者从“小白”迈向“高手”的必经之路。
为什么要学习数据结构与算法
|
2月前
|
算法系列之贪心算法
在算法中,贪心算法(Greedy Algorithm)是一种常见的解决优化问题的算法。贪心算法的核心思想是:在每一步选择中都采取当前状态下最优的选择,即贪心的做出局部最优的决策,从而希望最终能够得到全局最优解。尽管贪心算法并不总是能够得到全局最优解,但在许多实际问题中,它能够提供足够好的解决方案,并且具有较高的计算效率。
63 7
算法系列之贪心算法
架构学习:7种负载均衡算法策略
四层负载均衡包括数据链路层、网络层和应用层负载均衡。数据链路层通过修改MAC地址转发帧;网络层通过改变IP地址实现数据包转发;应用层有多种策略,如轮循、权重轮循、随机、权重随机、一致性哈希、响应速度和最少连接数均衡,确保请求合理分配到服务器,提升性能与稳定性。
487 11
架构学习:7种负载均衡算法策略
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
【EMNLP2024】基于多轮课程学习的大语言模型蒸馏算法 TAPIR
阿里云人工智能平台 PAI 与复旦大学王鹏教授团队合作,在自然语言处理顶级会议 EMNLP 2024 上发表论文《Distilling Instruction-following Abilities of Large Language Models with Task-aware Curriculum Planning》。
|
5月前
|
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
102 2
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
基于免疫算法的最优物流仓储点选址方案MATLAB仿真
本程序基于免疫算法实现物流仓储点选址优化,并通过MATLAB 2022A仿真展示结果。核心代码包括收敛曲线绘制、最优派送路线规划及可视化。算法模拟生物免疫系统,通过多样性生成、亲和力评价、选择、克隆、变异和抑制机制,高效搜索最优解。解决了物流仓储点选址这一复杂多目标优化问题,显著提升物流效率与服务质量。附完整无水印运行结果图示。
基于免疫算法的最优物流仓储点选址方案MATLAB仿真
基于GA遗传优化TCN时间卷积神经网络时间序列预测算法matlab仿真
本内容介绍了一种基于遗传算法优化的时间卷积神经网络(TCN)用于时间序列预测的方法。算法运行于 Matlab2022a,完整程序无水印,附带核心代码、中文注释及操作视频。TCN通过因果卷积层与残差连接学习时间序列复杂特征,但其性能依赖超参数设置。遗传算法通过对种群迭代优化,确定最佳超参数组合,提升预测精度。此方法适用于金融、气象等领域,实现更准确可靠的未来趋势预测。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等