问题描述

 有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且
          

∑i=1nwi≤c1+c2\sum^n_{i=1}w_i≤c_1+c_2i=1∑n​wi​≤c1​+c2​

问题:
是否有一个合理的装载方案,可将这n个集装箱装上这2艘轮船?如果有,找出一种装载方案。

例如:当n=3, c1=c2=50

(1)若w=[10, 40, 40]

   可将集装箱1和集装箱2装上第一艘轮船,而将集装箱3装上第二艘轮船;

(2)如果w=[20, 40, 40]

   则无法将这3个集装箱都装上船;

基本思路

已证明,如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。

  1. 首先将第一艘轮船尽可能装满;
  2. 将剩余的集装箱装上第二艘轮船。

  将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。由此可知,装载问题等价于以下特殊的0-1背包问题。

队列式分支限界法

  • 解装载问题的队列式分支限界法仅求出所要求的最优值,稍后进一步构造最优解。
  • 首先检测当前扩展结点的左儿子结点是否为可行结点。如果是,则将其加入到活结点队列Q中。
  • 然后,将其右儿子结点加入到活结点队列中(右儿子结点一定是可行结点)。2个儿子结点都产生后,当前扩展结点被舍弃。
  • 活结点队列中,队首元素被取出作为当前扩展结点。
  • 活结点队列已空,算法终止。

例子

例如 n=4, c1=12, w=[8, 6, 2, 3].


注:叶子结点不会被扩展,因此不用加入到活结点队列当中,此时,只需要检查该叶节点表示的最优解是否优于当前最优解,并实时更新当前最优解。

同层尾部标记:-1

活结点队列:
当取出的元素是-1时,判断当前队列是否为空,如果队列不空,则将尾部标记 -1加入到活节点队列中,代表算法开始处理下一层活节点,即:代表算法开始处理 下一个物品的装载问题(每一层i开始处理第i个物品的装载)。

代码(伪代码)

emplate<class Type>
Type MaxLoading(Type w[], Type c, int n)
{ //初始化
Queue<Type>Q; //  活结点队列
Q.Add(-1);  // 同层结点尾部标志
int i=1; //当前扩展结点所处的层
Type Ew=0; //扩展结点处相应的载重量bestw=0;
//搜索子集空间树
while (true) {// 检查左儿子结点if (Ew + w[i] <= c1) // x[i] = 1,Ew存储当前扩展结点相应的载重量EnQueue(Q, Ew + w[i], bestw, i, n); //将活结点加入到活结点队列Q中// 右儿子结点总是可行的,将其加入到Q中EnQueue(Q, Ew, bestw, i, n); // x[i] = 0Q.Delete(Ew);     // 取下一扩展结点if (Ew == -1) {      // 同层结点尾部if (Q.IsEmpty( )) return bestw;Q.Add(-1);        // 同层结点尾部标志Q.Delete(Ew);  // 取下一扩展结点i++;}                 // 进入下一层     }}

算法的改进

  算法MaxLoading初始时bestw=0,直到搜索到第一个叶结点才更新bestw。在搜索到第一个
叶结点前,总有Ew+r>bestw, 此时右子树测试不起作用。
  为确保右子树成功剪枝,应该在算法每一次进入左子树的时候更新bestw的值。

样例

分析演示

代码改进(伪代码)
while (true) {// 检查左儿子结点// wt=Ew + w[i];   // 左儿子结点的重量if (wt<= c) {     // 可行结点if (wt > bestw) bestw = wt;   //提前更新bestW,注意更新条件// 加入活结点队列if (i <= n) Q.Add(wt);}// 检查右儿子结点if (Ew + r > bestw && i <= n)   //右儿子剪枝Q.Add(Ew);     // 可能含最优解Q.Delete(Ew);     // 取下一扩展结点if (Ew == -1) {      // 同层结点尾部if (Q.IsEmpty()) return bestw;Q.Add(-1);        // 同层结点尾部标志Q.Delete(Ew);  // 取下一扩展结点i++;r-=w[i];}                 // 进入下一层      }  }

代码

#include <bits/stdc++.h>
using namespace std;
typedef struct QNode
{QNode *parent;int lchild;int weight;
}QNode;
int n;
int c;
int bestw;
int w[100];
int bestx[100];
void InPut()
{scanf("%d %d", &n, &c);for(int i = 1; i <= n; ++i)scanf("%d", &w[i]);
//    for(int i = 1; i <= n; ++i)
//        printf("%d ", w[i]);
//    cout << endl;
//    printf("输入结束\n");
}
//QNode *&bestE 的原因是 首先bestE是个地址, 其次引用为了赋值使用, 后边for循环中用到
void EnQueue(queue<QNode *> &q, int wt, int i, QNode *E, QNode *&bestE, int ch)
{if(i == n){if(wt == bestw){bestE = E;bestx[n] = ch;return;}}QNode *b;b = new QNode;b->weight = wt;b->lchild = ch;b->parent = E;q.push(b);
}
int MaxLoading()
{queue<QNode *>q;q.push(0);int i = 1;int Ew = 0, r = 0;bestw = 0;for(int j = 2; j <= n; ++j)r += w[j];QNode *E, *bestE; //bestE的作用是:结束while循环后,bestE指向最优解的叶子节点,然后通过bestE->parent找到装入了哪些物品。E = new QNode; //E这里作为一个中间量,连接parent和childE = 0;         //赋0是因为树的根的值是0,while刚开始的时候其代表rootwhile(true){int wt = Ew + w[i];if(wt <= c){if(wt > bestw)   //提前更新bestW,注意更新条件bestw = wt;EnQueue(q, wt, i, E, bestE, 1);}if(Ew + r >= bestw)   //右儿子剪枝{EnQueue(q, Ew, i, E, bestE, 0);    }E = q.front();q.pop();if(!E)    //如果取得的数是0,代表该处理下一层{if(q.empty())   //如果队列为空,表示该循环结束了break;q.push(0);     //如果队列中还有数据,表示循环还没结束。在该层的末尾加一个0标识符E = q.front();q.pop();i++;     //下一层走起r -= w[i];   //计算剩余的重量}Ew = E->weight; //不要忘记更新最新节点的值}for(int j = n - 1; j > 0; --j){bestx[j] = bestE->lchild;bestE = bestE->parent;}
}
void OutPut()
{printf("最优装载量为 %d\n", bestw);printf("装载的物品为 \n");for(int i = 1; i <= n; ++i)if(bestx[i] == 1)printf("%d ", i);
}
int main()
{InPut();MaxLoading();OutPut();
}

样例测试

数据为上面那个样例

输入
4 12
8 6 2 3

输出
最优装载量为 11
装载的物品为
1 4

优先队列式分支限界法

  • 解装载问题的优先队列式分支限界法用最大优先队列存储活结点表。
  • 活结点x在优先队列中的优先级定义为从根结点到结点x的路径所相应的载重量Ew(即:当前扩展结点船的载重量Ew)再加上剩余集装箱的重量r之和(即:将上界Ew+r定义为结点优先级)。
  • 优先队列中优先级最大的活结点成为下一个扩展结点。
  • 子集树中叶结点所相应的载重量与其优先级(上界值)相同,即:该叶子结点的上界值等于当前叶子结点处船的重量Ew。
  • 在优先队列式分支限界法中,一旦有一个叶结点成为当前扩展结点,则可以断言该叶结点所相应的解即为最优解。此时可终止算法。

求最优解

  • 在优先队列的每一个活结点中,保存从解空间树的根结点到该活结点的路径,在算法确定了达到最优值的叶结点时,就在该叶结点处同时得到相应的最优解。
  • 在算法的搜索进程中,保存当前已构造出的部分解空间树,这样在算法确定了达到最优值的叶结点时,可以在解空间树中从该叶结点开始向根结点回溯,构造出相应的最优解。

样例

分析演示

代码

#include <bits/stdc++.h>
using namespace std;
class MaxHeapQNode
{
public:MaxHeapQNode *parent;  //父节点int lchild;    //左节点:1; 右节点"0int weight;    //总重量int lev;       //层次
};
struct cmp
{bool operator()(MaxHeapQNode *&a, MaxHeapQNode *&b) const{return a->weight < b->weight;}
};
int n;
int c;
int bestw;
int w[100];
int bestx[100];
void InPut()
{scanf("%d %d", &n, &c);for(int i = 1; i <= n; ++i)scanf("%d", &w[i]);
}
void AddAliveNode(priority_queue<MaxHeapQNode *, vector<MaxHeapQNode *>, cmp> &q, MaxHeapQNode *E,  int wt, int i, int ch)
{MaxHeapQNode *p = new MaxHeapQNode;p->parent = E;p->lchild = ch;p->weight = wt;p->lev = i + 1;q.push(p);
}
void MaxLoading()
{priority_queue<MaxHeapQNode *, vector<MaxHeapQNode *>, cmp > q; // 大顶堆//定义剩余重量数组rint r[n + 1];r[n] = 0;for(int j = n - 1; j > 0; --j)r[j] = r[j + 1] + w[j + 1];int i = 1;MaxHeapQNode *E;int Ew = 0;while(i != n + 1){if(Ew + w[i] <= c){AddAliveNode(q, E, Ew + w[i] + r[i], i, 1);}AddAliveNode(q, E, Ew + r[i], i, 0);//取下一节点E = q.top();q.pop();i = E->lev;Ew = E->weight - r[i - 1];}bestw = Ew;for(int j = n; j > 0; --j){bestx[j] = E->lchild;E = E->parent;}
}
void OutPut()
{printf("最优装载量为 %d\n", bestw);printf("装载的物品为 \n");for(int i = 1; i <= n; ++i)if(bestx[i] == 1)printf("%d ", i);
}
int main()
{InPut();MaxLoading();OutPut();
}

样例测试

数据为上面那个样例

输入
4 12
8 6 2 3

输出
最优装载量为 11
装载的物品为
1 4

装载问题-分支限界法(队列式分支限界法,优先队列式分支限界法)相关推荐

  1. 分支界限算法【0-1背包问题】按照优先队列式(LC)分支限界法求解0-1背包问题, 并给出限界函数,并画出该实例的状态空间树。

    目   录 回溯算法[0-1背包问题] 分支界限算法[0-1背包问题] 作业题(期末考试必考) 小结 回溯算法[0-1背包问题] 分支界限算法[0-1背包问题] 解决思路:采用优先队列式分支限界 Ø ...

  2. 单源最短路径-分支限界法-优先队列式分支限界法-Dijkstra

    问题描述: 给定一个带权有向图G = (V, E), 其中每条边的权是非负实数.另外,还给定V中的一个顶点,称为源.现在要计算源到所有其他各定点的最短长度.这里路的长度是指路上各边权之和.这个问题通常 ...

  3. 线性数据结构的实现与应用_双端队列_逆波兰式_呼叫中心_XAUAT_(原问题自杜克大学Duke University cps110 C++ Stacks and Queues and Lists)

    求收藏求转发! 附实验报告链接: https://blog.csdn.net/weixin_43781565/article/details/106507340 提供DOC资源  https://do ...

  4. 链式队列,队列篇(链式队列的出队入队操作)

    数据结构,队列篇(链式队列) 前言: 上一篇博主简单讲解了顺序队列和循环队列,今天讲解队列最后一篇链式队,链式队在数据结构中用到比较多,用来做一些排队的算法,还有链式队列是也是和链式栈一样采用链表的方 ...

  5. 微服务-多线程-队列-设计模式-MySQL-分布式事务

    微服务-多线程-队列-设计模式-MySQL-分布式事务持续更新 周更 什么是微服务,微服务的好处 微服务的几个常用组件 Eurake Hytrix 服务熔断和降级 Ribbon Feign Gatew ...

  6. nyoj-257-郁闷的C小加(一 )中缀式变后缀式

    题目链接:here~~~~~~~ 今天看了此题,感觉栈和队列很好用,进一步深入了解 一个算术表达式,含有数字(为简化处理,数字只有一位),运算符:+.-.*,以及括号,求表达式的值. 给出的表达式是一 ...

  7. 直播预告 | 斯坦福助理教授马腾宇:深度学习中的隐式和显式正则化

    人工智能作为科技领域最具代表性的技术,日益成为国际竞争的新焦点.当下,我国正逐步开展全民智能教育普及,设置人工智能相关课程,致力于建设人工智能的人才高地. 在此背景下,中关村海华信息技术前沿研究院立足 ...

  8. Algorithm Gossip (22) 中序式转后序式(前序式)

    前言 This Series aritcles are all based on the book <经典算法大全>; 对于该书的所有案例进行一个探究和拓展,并且用python和C++进行 ...

  9. BC之链式块状结构:区块链之链式块状结构——链式块状结构、内容相关配图

    BC之链式块状结构:区块链之链式块状结构--链式块状结构.内容相关配图 目录 区块链之链式块状结构.内容相关配图 1.区块链结构 区块链之链式块状结构.内容相关配图 1.区块链结构

最新文章

  1. 浏览器复制不进行url编码_谷歌浏览器测试版支持在PC上复制号码并操作关联安卓设备进行拨打...
  2. VTK:相互作用之RubberBand3D
  3. springboot @RequestBody 接收字符串
  4. 闲谈网络运维管理的那些事儿(之三)
  5. 用国产CH32替代STM32,快来试试看!
  6. Java项目开发全流程实践
  7. http地址后面加上问号?防止IE缓存
  8. Atitit 人工智能 统计学 机器学习的相似性 一些文摘收集 没有人工智能这门功课,人工智能的本质是统计学和数学,就是通过机器对数据的识别、计算、归纳和学习,然后做出下一步判断和决策的科学
  9. CocosBuilder学习之一:认识CocosBuilder
  10. SPSS-论文常用格式-三线表
  11. 阿甘本:什么是当代人
  12. html 环形图圆角,canvas绘制圆角环形图
  13. Pr中有音频轨道,但是播放不了声音
  14. 图解Pandas,又一个Pandas学习利器!
  15. 网络设备配置与管理-综合实验2
  16. 世界上的第一台计算机什么样,世界上第一台计算机是什么样的
  17. HNU-计算机系统-讨论课5
  18. 史上最简单详细的Hadoop完全分布式集群搭建
  19. 【vscode 配置c++环境 win10】
  20. cad地图转成shp方法

热门文章

  1. 在虾皮shopee开店铺,我们要选什么样的类目?怎么做(上)
  2. windows10 store 错误代码为 0x80072EFD
  3. disconf mysql_Disconf配置中心配置
  4. MEXGroup:晚间专题技术报告2020-07-28
  5. 怎么读python代码_【怎么读python代码】作文写作问答 - 归教作文网
  6. java rgb转yuv_总结各种RGB转YUV的转换公式
  7. unity shader smoothstep and lerp ,InverseLerp
  8. 一键禁掉WIN10自动更新
  9. 网吧专用ros软路由教程(非常详细)
  10. 2022年第3周(1月10日-1月16日)中国各地区电影票房排行榜:所有地区票房环比均下降,2个疫情高风险地区票房低于10万元(附热榜TOP31详单)