目录
引入问题
解题思路
代码
引入问题
有一批共n个集装箱要装上载重量为C1的轮船,其中集装箱i的重量为Wi,且要求确定是否有一个合理的装载方案可将这n个集装箱装上这艘轮船。
解题思路
利用分支界限算法的思想解决此问题,用到队列式分支界限法。如图所示:
图中,左孩子表示选择,右孩子表示未被选择,用广度优先遍历队列,记录每次的cw(已选择的重量)以及bestw(最多可以选择的重量)。
代码
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <queue>
using namespace std;
/*
有一批共n个集装箱要装上重量分别为C1的轮船。
其中集装箱i的重量为wi,且要求确定是否有一个合理的装
载方案可将这n个集装箱装上这艘轮船。
*/
int w[] = { 12, 8, 15 };//集装箱重量
const int n = sizeof(w) / sizeof(w[0]);
int c = 10;//轮船的容量
int cw = 0;//已选物品的重量
int bestw = 0;//记录最优装载量
int r = 0;//记录未被选择的集装箱的总重量
//描述节点类型
struct Node
{
Node(int w, int l, Node *p, bool left)
{
weight = w;
level = l;
parent = p;
isleft = left;
}
int weight;//从根节点到当前节点所选物品总重量
int level;//当前节点层数
bool isleft;//记录当前节点是否被选择
Node *parent;//记录当前节点的父节点
};
//广度优先遍历FIFO队列
queue <Node*> que;
int i = 0;//起始层数,从根节点开始
Node *bestnode = nullptr;
void addLiveNode(int w,int level,Node *parent,bool isleft)
{
Node *node = new Node(w, level, parent, isleft);
que.push(node);
//在最后一层记录最优节点
if (level == n && w == bestw)
{
bestnode = node;
}
}
int maxBound(int level)
{
int s = 0;
for (int j = level + 1; j < n; ++j)
{
s += w[j];
}
return s;
}
int main()
{
Node *node = nullptr;
while (i < n)
{
//处理左孩子 选择集装箱
int wt = cw + w[i];
if (wt <= c)//可以装入轮船
{
if (wt > bestw)
{
bestw = wt;
}
//que.push(Node(cw + w[i], i + 1));
addLiveNode(cw + w[i], i + 1, node, true);
}
r = maxBound(i);
if (cw + r >= bestw) //这里操作 = 不能少 否则不能处理到叶子节点 例如c=20
{
//que.push(Node(cw, i + 1));//处理右孩子 不选择集装箱
addLiveNode(cw, i + 1, node, false);
}
node = que.front();
que.pop();
cw = node->weight;
i = node->level;
}
cout << bestw << endl;
int bestx[n] = { 0 };
for (int j = n - 1; j >= 0; --j)
{
bestx[j] = bestnode->isleft ? 1 : 0;
bestnode = bestnode->parent;
}
for (int v : bestx)
{
cout << v << " ";
}
cout << endl;
system("pause");
return 0;
}
在这段代码中对代码进行了我们之前在回溯算法提到的剪枝操作,为了提高代码效率。
在右子树中,当我们已经选择的重量加上剩余未被选择的总重量小于我们此时记录的bestw时,我们就不必在对此树枝进行操作了。实现代码如下:
结果如下:
结果中,第一行表示最优可选择的重量,第二行表示选择的集装箱(1表示选择,0表示未被选择)
- 当轮船容量为27
- 当轮船容量为20
- 当轮船容量为10