装箱问题(贪婪策略:首次适应递减(First Fit Decreasing, FFD))
装箱问题是一种典型的组合优化问题,它可以用多种贪婪(greedy)策略来解决。贪婪算法通过在每一步选择当前最优的解决方案,希望这样会导致全局最优解。
问题描述:假设有编号分别为0,1,…,n-1的n种物品,体积分别为V0,V1,…,Vn-1。将这n种物品装到容量都为V的若干箱子里。约定这n种物品的体积均不超过V,即对于0≤ i<n,有0<Vi ≤V。不同的装箱方案所需要的箱子数目可能不同。“装箱”问题要求用尽量少的箱子装下这n种物品。
代码如下:
//装箱问题
#include <stdio.h>
#include <malloc.h>
#define V 10 //一个箱子所能装的最大体积//物品信息
typedef struct
{int gnum; //物品编号int gv; //物品体积
}Goods;//物品链
typedef struct Node
{int gnum;struct Node *next; //连接下一个物品
}GoodsLink;//箱子链
typedef struct Box
{int bv; //箱子体积struct Box *next; //下一个箱子struct Node *hg; //箱子上的物品节点
}BoxLink;//物品体积降序排列
//冒泡排序法进行排序
Goods *SortGoods(Goods *g,int n)
{for(int i=0;i<n-1;i++){for(int j=0;i+j<n-1;j++){if(g[j+1].gv>g[j].gv){Goods t=g[j];g[j]=g[j+1];g[j+1]=t;}}}return g;
}//装箱
BoxLink *CreateBoxLink(Goods *g,int n)
{GoodsLink *pg,*qg;BoxLink *pbox,*hbox=NULL,*t;for(int i=0;i<n;i++){//创建物品节点pg=(GoodsLink *)malloc(sizeof(GoodsLink));pg->gnum=g[i].gnum;pg->next=NULL; //判断是否需要创建新的箱子节点//判断条件:箱子结点不为空&&该物品的体积大于箱子所剩余的体积for(pbox=hbox;pbox&&(pbox->bv<g[i].gv);pbox=pbox->next); //如果没有找到合适的箱子创建箱子节点if(!pbox){//创建新的箱子节点pbox=(BoxLink *)malloc(sizeof(BoxLink));pbox->bv=V;pbox->hg=NULL;pbox->next=NULL;if(!hbox) //判断箱子链表是否为空hbox=t=pbox;elset=t->next=pbox; } //不执行if:表示有箱子可以放的下物品pbox->bv-=g[i].gv; //用剩余箱子体积减去当前的物品体积if(!pbox->hg) //在当前箱子上挂物品,判断箱子上是否有物品pbox->hg=pg; //该物品是这个箱子的第一个物品节点else {for(qg=pbox->hg;qg->next;qg=qg->next); //找到挂物品所要挂的位置qg->next=pg; //将物品挂在找到的节点上}}return hbox;
}//输出每个箱子所装的物品
void PrintBox(BoxLink *hbox)
{int cnt=0;for(BoxLink *pbox=hbox;pbox;pbox=pbox->next){printf("第%d个箱子所放的物品编号:",++cnt); for(GoodsLink *pg=pbox->hg;pg;pg=pg->next)printf("%2d",pg->gnum); printf("\n"); }printf("\n");
} int main(void)
{int n,v;Goods *g;BoxLink *hbox;printf("请输入物品的个数:");scanf("%d",&n);printf("\n");g=(Goods *)malloc(n*sizeof(Goods)); //定义物品信息//初始化物品信息for(int i=0;i<n;i++){printf("请输入第%d件物品体积: ",i+1);scanf("%d",&v);g[i].gv=v;g[i].gnum=i+1;}printf("\n");g=SortGoods(g,n); //物品体积降序排列hbox=CreateBoxLink(g,n); //装箱PrintBox(hbox); //输出每个箱子所装的物品return 0 ;
}
总体流程
- 输入物品信息:首先获取物品个数和每个物品的体积,并将它们存储在一个结构体数组中。
- 物品排序:然后对物品按体积进行降序排列,使得大物品优先被处理。
- 装箱:根据物品的大小和箱子的剩余容量来决定物品的放置。
- 输出结果:最后打印出每个箱子中包含的物品编号。
关键组件
- 物品(Goods):包括物品编号
gnum
和物品体积gv
的结构体。 - 物品链(GoodsLink):用于链表形式存储每个箱子内的物品编号。
- 箱子链(BoxLink):表示每个箱子,包含该箱子剩余体积
bv
、指向下一个箱子的指针next
和指向该箱子内第一个物品节点的指针hg
。
详细解释
物品体积降序排列 SortGoods
使用冒泡排序算法对物品按体积进行降序排列。这样做的目的是尽可能优先处理大物品,便于后续的装箱操作。
装箱 CreateBoxLink
- 遍历每个物品,为其创建一个物品链节点。
- 尝试找到一个可容纳当前物品的箱子(箱子的剩余体积足够大)。
- 如果找到合适的箱子,则更新箱子的剩余体积,并将该物品节点加入箱子的物品链中。
- 如果没有找到合适的箱子,则新建一个箱子,并将其加入箱子链中,然后重复上述步骤。
- 这个过程采用贪心策略,尽量在已有的箱子中放置物品,以减少总箱子数。
输出结果 PrintBox
遍历箱子链,对于每个箱子,打印出其中包含的所有物品编号。这显示了每个箱子中物品的装载情况。
注意事项
- 在
SortGoods
函数中,循环条件有误:for(int j=0;i+j<n-1;j++)
应改为for(int j=0;j+i<n-1;j++)
,否则会造成无限循环或跳过排序。 - 使用
malloc
分配内存时需要注意内存管理,确保在使用完毕后释放已分配的内存,避免内存泄漏。本代码未展示内存释放过程。
如果觉得文章对您有帮助,请帮忙点赞或者收藏,如果在文章中发现什么错误或不准确的地方,欢迎与我交流。