【问题描述】
集装箱装载问题要求确定在不超过轮船载重量的前提下,将尽可能多的集装箱装上轮船。
在前面的“装载问题”中介绍了回溯算法的应用。
输入样例
80 4
18 7 25 36
输出样例
79
装载问题的解空间是一棵子集树,采用队列式分支限界法来解决。该算法只求出所要求的最优值。
【算法分析】
1、队列式分支限界法
定义一个先进先出(FIFO)队列Q,初始化队列时,在尾部增加一个-1标记。
这是一个分层的标志,当一层结束时,在队列尾部增加一个-1标志。
定义扩展结点相应的载重量为Ew,剩余集装箱的重量为r,当前最优载重量为bestw,轮船的载重量为c=80。
算法从子集树的第0层开始展开。
第0层即集装箱0的重量w[0]=18,是否装入轮船的两种状态。
在第0层,Ew=0,bestw=0,r=w[1]+w[2]+w[3]=68,Ew+w[0]<c,Ew+r>bestw,结点B和C依次进入队列。
从队列中取出活结点—1,由于队列不为空,表示当前层结束,新的一层开始,在队列尾部增加一个-1标记。
从队列中取出活结点Ew=18,即结点B。
第1层即集装箱1的重量w[1]=7,bestw=18,r=w[2]+w[3]=61,Ew+w[1]=25<c,Ew+r=79>bestw,结点D和E依次进入队列。
从队列中取出活结点Ew=0,即结点C。
bestw=25,r=w[2]+w[3]=61,由于Ew+w[1]=7<c, Ew+r=61>bestw,结点F和G依次进入队列。
从队列中取出活结点—1,由于队列不为空,表示当前层结束,新的一层开始,在队列尾部增加一个-1标记。
2、数据结构
//(1)装载问题分支限界算法的数据结构
#define NUM 100
int n; //集装箱的数量
int c; //轮船的载重量
int w[NUM]; //集装箱的重量数组
3、分支界限算法的实现
//(2)装载问题分支限界算法的实现
int MaxLoading()
{
queue<int> Q;
Q.push(-1);
int i = 0;
int Ew = 0;
int bestw = 0;
int r = 0;
for(int j=1; j<n; j++)
r += w[j];
//搜索子空间树
while (true)
{
//检查左子树
int wt = Ew+w[i];
if (wt<=c) //检查约束条件
{
if (wt>bestw) bestw = wt;
//加入活结点队列
if (i<n-1) Q.push(wt);
}
//检查右子树
//检查上界条件
if (Ew+r>bestw && i<n-1)
Q.push(Ew);
//从队列中取出活结点
Ew = Q.front();
Q.pop();
if (Ew==-1) //判断同层的尾部
{
if (Q.empty()) return bestw;
//同层结点尾部标志
Q.push(-1);
//从队列中取出活结点
Ew = Q.front();
Q.pop();
i++;
r -= w[i];
}
}
return bestw;
}
【算法的完整实现】
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
#define NUM 100
int n;
int c;
int w[NUM];
int MaxLoading()
{
queue<int> Q;
Q.push(-1);
int i = 0;
int Ew = 0;
int bestw = 0;
int r = 0;
for(int j=1; j<n; j++)
r += w[j];
while (true)
{
int wt = Ew+w[i];
if (wt<=c)
{
if (wt>bestw) bestw = wt;
if (i<n-1) Q.push(wt);
}
if (Ew+r>bestw && i<n-1) Q.push(Ew);
Ew = Q.front();
Q.pop();
if (Ew==-1)
{
if (Q.empty()) return bestw;
Q.push(-1);
Ew = Q.front();
Q.pop();
i++;
r -= w[i];
}
}
return bestw;
}
int main()
{
while(cin>>c>>n)
{
for(int i=0; i<n; i++)
cin>>w[i];
int ans = MaxLoading();
if (ans) cout<<ans<<endl;;
else cout<<"No answer!"<<endl;
}
return 0;
}