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

简介: 背包问题解析

一、前言

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

二、背包问题

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

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

谢谢大家!

目录
相关文章
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
|
21天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
38 2
|
26天前
|
机器学习/深度学习 人工智能 自然语言处理
【EMNLP2024】基于多轮课程学习的大语言模型蒸馏算法 TAPIR
阿里云人工智能平台 PAI 与复旦大学王鹏教授团队合作,在自然语言处理顶级会议 EMNLP 2024 上发表论文《Distilling Instruction-following Abilities of Large Language Models with Task-aware Curriculum Planning》。
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
1天前
|
算法
基于GA遗传算法的PID控制器参数优化matlab建模与仿真
本项目基于遗传算法(GA)优化PID控制器参数,通过空间状态方程构建控制对象,自定义GA的选择、交叉、变异过程,以提高PID控制性能。与使用通用GA工具箱相比,此方法更灵活、针对性强。MATLAB2022A环境下测试,展示了GA优化前后PID控制效果的显著差异。核心代码实现了遗传算法的迭代优化过程,最终通过适应度函数评估并选择了最优PID参数,显著提升了系统响应速度和稳定性。