贪心算法–装箱问题
还在持续学习中,如有什么纰漏,请在评论区加以指正……
简单认识贪心算法
贪心算法,在我最初的理解里就是贪心,什么都想要最好的。那么,这就正符合了贪心算法在运用过程中的特点–即贪心算法总是做出当前看来是最好的选择。这就意味着贪心算法并不从整体最优上考虑,而只是在当前情况下的最优选择。
贪心准则
- 贪心准则是指在解决问题的过程中将解决这一个问题的n个步骤的每一步都抽象出其公共的最优解。
注:这就是贪心算法所阐述的要求每一步都最优,每一步都能得到当前情况下的最优解。
- 一个问题中只有一个贪心准则。
注:那就意味着只要在解决问题过程中有其中一步解决问题的办法与前面不同,那就得推翻这一个贪心准则,重新找出解决问题步骤的公共最优解决办法。
总而言之呢,贪心算法最大的特点就是在解决问题的时候最简单,最快捷,最高效的得到一个近似最优解的结果。
装箱问题
接下来,以一个典型的装箱问题来进一步理解一下贪心算法。
问题描述:
- 假设有n个物品,其体积为:V1,V2,V3……Vn;
- 有若干个体积为V的箱子。
要求:将这n个物品全装到箱子里,使得打开的箱子尽可能地少。
!!!注意哈,要求上说使得打开的箱子尽可能地少,而不是最少,那么这就是典型的贪心算法可以解决的问题,求出一个近似最优解的解。
思考:我们怎样才能合理化分配箱子的空间使得装进的物品尽可能多,从而打开的箱子也就相应的尽可能地少呢?第一反应肯定是尽着大的装,所以我们首先要对物品进行排序,使得体积大的物品排在前面。第一个箱子装的是最大体积的物品,那么第二个物品要再装箱的时候就要进行比较。第一个箱子的剩余空间装得下这第二个物品吗?装得下我们就直接装进去,装不下我们就只能再重新打开一个箱子。第三个物品跟第二个物品装箱的步骤一样,从第一个箱子开始遍历,第一个箱子剩下的空间够不够,够的话就装,不够就看第二个箱子够不够,够的话就装,不够就要打开新的箱子了。依次类推,第n个物品装箱的时候依旧从第一个箱子开始遍历,剩余的空间够不够呀,够的话就装进去,不够,下一个进行比较,直到所有的箱子的剩余空间都不够的时候就打开新的箱子。
算法描述:
1.存储结构
我们在考虑存储结构的时候一定要先对问题对象进行抽象化,就拿这个问题来讲,我们有n个物品,若干个箱子,那么简单直接的就可以看出一定会有物品和箱子这两种类型。那么我们再细化问题:
i> 物品,n个物品则为一个有确定数量的对象,那么我们在存储这类具有确定数量的对象时,就可以采用数组来进行存储,而物品又包括有物品编号,物品的体积,那么这个数组显然就是一个结构体数组。声明如下:
typedef struct node{
int gNum; //物品的编号
int gV; //物品的体积
}Goods;
ii>箱子,若干个箱子,那么这个箱子的数量就不是确定的,所以我们需要采用链表的结构来存储箱子。而箱子又包括箱子剩余的体积,箱子的next指向,箱子装进的物品的编号。
!!!注意了!!!我们只在箱子里存储装入物品的编号,可以直接利用这个编号在物品的信息中找到它所对应的体积。如果将物品所有信息都存储进来,那么就造成了数据的重复存储,从而浪费内存。然而在存储物品编号的时候我们显然不会在这个箱子里只装一个物品,所以,这里就需要另外一个单独的装入物品的类型,我们把它在这里叫做物品编号。这个物品编号的类型里面存储所要装进箱子的物品编号以及它的next的指向。所以,它的声明如下:
typedef struct goodsLink{
int gNum; //物品的编号
struct goodsLink *link; //物品的next指向
}GoodsLink;所以,我们的箱子的声明如下:
typedef struct box{
int restV; //箱子剩余的体积
GoodsLink * hg; //装进的物品的结点
struct box * next;//箱子的next指向
}BoxLink;
综上所述,我们需要有三个存储的对象,声明也已经写过了。接下来我们就要解决问题了。
2.解决过程
i>第一步当然要进行初始化,我们要把每一个物品的信息都写完整。
Goods * g;
g=(Goods *)malloc(N*sizeof(Goods)); //创建一个N个长的数组来存储物品
for