您的位置:首页 > 其它

回溯算法;双船装载问题;限界+约束;子集树;时间复杂度:O(2的n次方);

2010-04-02 09:09 471 查看
#include <iostream>
using namespace std;

class MaxLoading
{
private:
int numOfItems;//物品数量
int *weight;//物品重量
int boatSpace1;//船1的容量
int boatSpace2;//船2的容量
int *best;//记录最优的物品选择情况
int *path;//记录递归路径
int bestWeight;//记录最优的重量
int leftWeight;//剩余物品总重量
int boatWeight;//船已装载的重量
public:
//构造函数
MaxLoading(int numOfItems,int boatSpace1,int boatSpace2)
{
this->numOfItems=numOfItems;
this->boatSpace1=boatSpace1;
this->boatSpace2=boatSpace2;
this->path=new int[numOfItems+1];

weight=new int[numOfItems+1];
best=new int[numOfItems+1];
bestWeight=0;//初始化最优载重为0
boatWeight=0;//初始化船已载重0
leftWeight=0;
}
//输入物品数量
void input()
{
int i=1;
while(i<=numOfItems)
{
cout<<"输入第"<<i<<"个物品的重量"<<endl;
cin>>weight[i];
leftWeight+=weight[i];//初始化为总重量
best[i]=0;//初始化为都不装在
++i;
}
}
//回溯算法完成装载问题,按递归树的层深度优先遍历
//装载船1
void loading1(int i)
{
if(i>numOfItems)//如果已经全部遍历过,由于限界函数的作用,boatWeight一定优于bestWeight,所以直接更新
{
bestWeight=boatWeight;
for(int j=1;j<=numOfItems;j++)
{
best[j]=path[j];
}
return;
}
//如果没有遍历结束
//如果剩余容量足以装入,那么就装入
boatWeight+=weight[i];
leftWeight-=weight[i];

if(boatSpace1>=boatWeight)//约束函数剪枝
{
path[i]=1;
loading1(i+1);
}
//也可以选择不装入
boatWeight-=weight[i];
if(boatWeight+leftWeight>bestWeight)//限界函数剪枝
{
path[i]=0;
loading1(i+1);
}
//恢复现场
leftWeight+=weight[i];
}
//装载船2
void loading2(int i)
{
if(i>numOfItems)//如果已经全部遍历过,由于限界函数的作用,boatWeight一定优于bestWeight,所以直接更新
{
bestWeight=boatWeight;
for(int j=1;j<=numOfItems;j++)
{
best[j]=path[j];
}
return;
}
//如果没有遍历结束
//如果剩余容量足以装入,那么就装入
boatWeight+=weight[i];
leftWeight-=weight[i];

if(boatSpace2>=boatWeight)//约束函数剪枝
{
path[i]=1;
loading2(i+1);
}
//也可以选择不装入
boatWeight-=weight[i];
if(boatWeight+leftWeight>bestWeight)//限界函数剪枝
{
path[i]=0;
loading2(i+1);
}
//恢复现场
leftWeight+=weight[i];
}
//调用回溯算法,完成最优装载问题
void maxLoading()
{
loading1(1);
//打印船1结果,准备装载船2
numOfItems=display1();
bestWeight=0;//重置船2最优载重量为0
boatWeight=0;//重置船2已装载重量为0
for(int i=1;i<=numOfItems;i++)
{
best[i]=0;
}
loading2(1);//装载船2
display2();
}
//打印船1装载结果
int display1()
{
cout<<"船1最大装载重量:"<<bestWeight<<endl;
int i=1;
int j=0;
while(i<=numOfItems)
{
cout<<"物品重量"<<weight[i]<<"是否装载:"<<best[i]<<endl;
if(best[i]==0)
{
weight[++j]=weight[i];
}
else
{
leftWeight-=weight[i];//减去已经放在船1上的重量
}
++i;
}
cout<<endl;
return j;
}
//打印船2装载结果
void display2()
{
cout<<"船2最大装载重量:"<<bestWeight<<endl;
int i=1;
while(i<=numOfItems)
{
cout<<"物品重量"<<weight[i]<<"是否装载:"<<best[i]<<endl;
++i;
}
}

};

void main()
{
MaxLoading test(4,50,80);//4个物品,船1最大载重50,船2最大载重80
test.input();//输入4个物品的重量
test.maxLoading();//对船1,船2进行装载
}


 

装载问题可以抽象成一颗子集树,对于每一个结点,只有两种选择,0或者1,一共有n层,即n个物品,所以问题的解可以有O(2的n次方)规模.

 

回溯法与普通的递归区别在于剪枝函数的应用.

 

剪枝函数在这个问题上很明显的分为两种:

 

1.约束函数,即显示约束函数.  这个问题里,通过检查当前i物品装载入船后的重量是否超过船的容量为约束,如果装载进去以后超过,那么很明显物品i不能放入,所以只能走不选择的那条路径。 

 

2.限界函数, 可以叫做隐式约束吧。  在这个问题里,由于我们要求最大载重量,如果物品i放入船内,那么它依旧有可能在接下来的物品寻则中超越最优载重量。

如果物品i不放入船内,这时候需要检测物品i之后的物品的总重量+现在已经装入的重量能否超越最优载重,如果这样的上界都难以满足条件,则说明即便剩下的物品都装进去都不会超过最优的,那么就不必从这个结点继续递归下去了。 这时候,我们要不把i放进去再递归下去,要不回溯到上一层再做其他选择。  这也就是回溯了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 path include input