问题描述
有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且
问题:
是否有一个合理的装载方案,可将这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背包问题。
算法思路
用树
表示解空间,则解为n元向量{x1, … ,xn }, xi∈{0, 1} 。
约束条件:
当前搜索的层i <= n时,当前扩展结点Z为子集树的内部结点,仅当满足cw+w[i] <= c
时进入左子树,x[i]=1; 当cw+w[i] > c
,在以结点Z为根的子树中所有结点都不满足约束条件,因而该子树中解都是不可行解,因而将在该子树删去。
限界函数:
由于是最优化问题, 可利用最优解性质进一步剪去不含最优解的子树:
设Z是解空间树第i层上的当前扩展结点。
设 bestw: 当前最优载重量,
cw : 当前扩展结点Z的载重量 ;
r : 剩余集装箱的重量;
在以Z为根的子树中任意叶结点所相应的载重量不超过cw + r。因此,当cw + r (限界函数) ≤ bestw时,可将Z的右子树剪去。即:cw + r > bestw 时搜索右子树,x[i]=0;
例子
演示分析过程
核心代码
template<classType>
void Loading<Type>::Backtrack(int i)
{ / /搜索第i层结点
if (i>n) {//到达叶结点
bestw=cw;
return;
}
r - = w[i]; //搜索子树
if (cw+w[i]<=c){ //搜索左子树
x[i]=1;
cw += w[i];
Backtrack (i+1);
cw - = w[i];
}
if (cw+r > bestw){ //搜索右子树
x[i]=0;
Backtrack(i+1);
}
r+=w[i]
代码
#include <bits/stdc++.h>
using namespace std;
int n; //集装箱数
int cw; // 当前载重量, current weight
int bestw; //最优载重重量
int r; //剩余集装箱重量
int c1; //第一艘轮船的载重量
int c2; //第二艘轮船的载重量
int x[100]; //当前解
int bestx[100]; //当前最优解
int w[100]; //集装箱重量数组
void OutPut()
{
int restweight = 0;
for(int i = 1; i <= n; ++i)
if(bestx[i] == 0)
restweight += w[i];
if(restweight > c2)
cout<<"不能装入"<<endl;
else
{
cout<<"船1装入的货物为:";
for(int i = 1; i <= n; ++i)
if(bestx[i] == 1)
cout<<" "<<i;
cout<<endl<<"船2装入的货物为:";
for(int i = 1; i <= n; ++i)
if(bestx[i] != 1)
cout<<" "<<i;
}
}
void BackTrack(int i)
{
if(i > n)
{
if(cw > bestw)
{
for(int i = 1; i <= n; ++i)
bestx[i] = x[i];
bestw = cw;
}
return;
}
r -= w[i];
if(cw + w[i] <= c1) //约束条件
{
cw += w[i];
x[i] = 1;
BackTrack(i + 1);
x[i] = 0;
cw -= w[i];
}
if(cw + r > bestw) //限界函数
{
x[i] = 0;
BackTrack(i + 1);
}
r += w[i];
}
void Initialize()
{
bestw = 0;
r = 0;
cw = 0;
for(int i = 1; i <= n; ++i)
r += w[i];
}
void InPut()
{
cout<<"请输入箱子个数:";
cin>>n;
cout<<"请输入两艘船的最大载重量:";
cin>>c1>>c2;
cout<<"请输入箱子的重量:";
for(int i = 1; i <= n; ++i)
cin>>w[i];
}
int main()
{
InPut();
Initialize();
BackTrack(1);
OutPut();
}
测试样例
输入
请输入箱子个数:4
请输入两艘船的最大载重量:100 100
请输入箱子的重量:90 10 80 10
输出
船1装入的货物为: 1 2
船2装入的货物为: 3 4
输入
请输入箱子个数:4
请输入两艘船的最大载重量:100 100
请输入箱子的重量:90 20 90 10
输出
不能装入