回溯算法;双船装载问题;限界+约束;子集树;时间复杂度: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放进去再递归下去,要不回溯到上一层再做其他选择。 这也就是回溯了。
相关文章推荐
- 符号三角形问题;回溯算法;子集树问题;时间复杂度O(2的n次方);
- 一道时间复杂度为O(N)空间复杂度为O(1)的排序问题
- 3.mysql的中文问题,database级操作,表级操作,数据CRUD,分组操作,时间和日期,字符串相关函数,表的约束
- 《编程之美》买书问题,时间复杂度的分析
- 常见问题与常见算法的时间复杂度
- 【c++】关于时间复杂度和空间复杂度的相关问题
- 一个时间复杂度的问题
- 【开放 5月18日 发布】:子数组的最大乘积问题_____问题简单,请思考如何逐步降低时间复杂度,跟帖回复,群内讨论.
- 数据结构排序问题---堆排序及各种排序时间空间复杂度
- 什么是时间复杂度,什么是P问题、NP问题和NPC问题
- 选择问题(线性时间复杂度)
- 最大子序列和问题(4种方法,按时间复杂度)
- 动态规划 O(n)时间复杂度的找零钱问题
- 最长公共子序列求解和优化等问题(未完善时间复杂度的优化)
- 《编程之美》中买书问题算法。空间复杂度O(n),时间复杂度O(n),求挑战
- 关于算法的时间复杂度问题的思考
- 排序的最好和最坏的时间复杂度问题
- 数据结构和算法学习系列之最大子序列求和问题的O(N)时间复杂度
- 回溯算法之最优装载问题
- 选择问题(线性时间复杂度)