1. 问题描述:有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且集装箱重量总和(w1+w2+…+wn)<c1+c2。试找出一种合理的装载方案将这n个集装箱装上这2艘船。
2. 问题分析:如果该装载问题有解,装载策略为:
(1) 首先将第一艘船尽可能装满(等价于特殊的0-1背包问题,可采用动态规划算法);
(2) 然后将剩余的集装箱都装上第二艘轮船;
3. 解题思想:分析可知,使用子集树表示其解空间(在从n个元素的集合中找到满足条件的子集),剪枝函数:记录当前结点处已经到达的装载重量cw,若cw>c1,即以该结点为根的子树中所有结点都不满足条件,直接剪枝即可。
#include <iostream>
using namespace std;
int bestw=0;//当前最优载重量
int c=50;
int N=4;
int y[4];
int r;//剩余集装箱的重量
void Backtrack(int i,int cw,int w[],int x[])
{
if(i>=N)//如果到达叶子结点
{
if(cw>bestw)
{
bestw=cw;
for(int i=0;i<N;i++)
y[i]=x[i];
}
return;
}
r-=w[i];
//搜索子树
if(cw+w[i]<=c)
{
x[i]=1;
cw+=w[i];
Backtrack(i+1,cw,w,x);
cw-=w[i];
x[i]=0;
}
if(cw+r>bestw) //上界函数,保证到达叶结点时候的cw都优于当前的最优解bestw
{
x[i]=0;
Backtrack(i+1,cw,w,x);
}
r+=w[i];
}
int main()
{
int w[]={10,20,30,30};
int x[N];
for(int i=0;i<N;i++)
r+=w[i];
Backtrack(0,0,w,x);
for(int i=0;i<N;i++)
cout<<y[i]<<" ";
cout<<endl;
cout<<bestw<<endl;
}
分析可知,bestx可能被更新O(2^n)次,所以时间复杂性为O(n*2^n);
改进策略:首先只运行计算最优值的算法,计算出最优装载量bestw,此时需要O(2^n)时间,然后再运行Backtrack方法,将bestw值固定为已经计算出来的最优值,当首次到达叶子结点的时候终止算法,此时得到的x[]就是最优解。