算法分析与设计实验报告
第二次附加实验
姓名学号班级时间12.12上午地点工训楼309实验名称回溯法实验(最优装载)
实验目的1.掌握回溯法求解问题的思想
2.学会利用其原理求解相关问题
实验原理基本思想:
用回溯法解题的一个显著特征是在搜索过程中动态产生问题的解空间。
在任何时刻,算法只保存从根结点到当前扩展结点的路径。
如果解空间树中从根结点到叶结点的最长路径的长度为h(n),则回溯法所需的计算空间通常为O(h(n))。
而显式地存储整个解空间则需要O(2h(n))或O(h(n)!)内存空间。
基本解题步骤:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
实验步骤(1)首先将第一艘轮船尽可能装满;
(2)将剩余的集装箱装上第二艘轮船;
(3)用可行性约束函数可剪去不满足约束条件的子树;(4)定义上界函数为cw+r。
在以z为根的子树中任一叶结点所相应的载重量均不超过cw+r。
因此,当cw+r<=bestw时,可将z的右子树剪去。
关键代码template<class Type>
void Loading<Type>::Backtrack(int i) //搜索第i层结点{
if(i>n) { //到达叶结点
if(cw>bestw){
for(int j=1;j<=n;j++){
bestx[j]=x[j]; //更新最优解
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];
}
当输入的数据有解时:
测试结果
当输入的数据无解时:
附录:
完整代码(贪心法)
//回溯法 递归求最优装载问题
实验分析
在实验中并没有生成多组数据,进行比较,也没有利用随机生成函数,因为在这种有实际有关联的问题中,利用随机生成函数生成的数据是十分的不合适的,在此我们只需要验证该程序是否正确即可。
在这个实验中其实际上是一种特殊的0-1背包问题,我们在第一艘栓船上就是利用0-1背包的思想,第二
艘船只需要将剩余货物的重量和第二艘船的载重量相比较就可以了,如果可以
装下就说明有解,否则就是无解。
由于数据较小,所以时间上并不能反映出什
么东西。
实验心得
在这一章的回溯算法中,我们用的比较多的就是;利用子集树来进行问题
的探索,就例如上图是典型的一种子集树,在最优装载、0-1背包都是利用了
这种满二叉树的子集树进行求解,然后通过深度优先的策略,利用约束函数和
上界函数,将一些不符合条件或者不包含最优解的分支减掉,从而提高程序的
效率,通过实现编程实现该问题,是我对于这一问题;理解的更加明白,果然
动手去实现才是一种好的学习的习惯,怪不得确实在动手的同学学的比较好
呢,以后我也要多努力啊。
实验得分 助教签名
#include<iostream>
#include<time.h>
#include<iomanip>
using namespace std;
template<class Type>
class Loading
{
public:
void Backtrack(int i);
int n, //集装箱数
*x, //当前解
*bestx; //当前最优解
Type *w, //集装箱重量数组
c, //第一艘轮船的载重量
cw, //当前载重量
bestw, //当前最优载重量
r; //剩余集装箱重量
};
template<class Type>
void Loading<Type>::Backtrack(int i);
template<class Type>
//参数为:w[]各物品重量数组,c为第一艘轮船的载重量,n为物品数量,bestx[]数组为最优解
Type MaxLoading(Type w[],Type c,int n,int bestx[]);
int main()
{
int n=3,m;
int c=50,c2=50;
int w[4]={0,10,40,40};
int bestx[4];
clock_t start,end,over; //计算程序运行时间的算法
start=clock();
end=clock();
over=end-start;
start=clock();
m=MaxLoading(w,c,n,bestx); //调用MaxLoading函数
cout<<"轮船的载重量分别是:"<<endl;
cout<<"c(1)="<<c<<",c(2)="<<c2<<endl; //输出两个船的装载量
cout<<"待装集装箱重量分别为:"<<endl; //输出待装货物的重量
cout<<"w(i)=";
for(int i=1;i<=n;i++)
{cout<<w[i]<<" ";}
cout<<endl;
cout<<"回溯选择结果:"<<endl; //输出第一艘船的实际装载量
cout<<"m(1)="<<m<<endl;
cout<<"x(i)=";
for(int i=1;i<=n;i++) //输出是那种货物装到第一艘船上
{cout<<bestx[i]<<" ";}
cout<<endl;
int m2=0;
for(int j=1;j<=n;j++)
m2=m2+w[j]*(1-bestx[j]); //计算剩余的货物重量cout<<"m(2)="<<m2<<endl;
if(m2>c2)
{cout<<"因为m(2)大于c(2),所以原问题无解!"<<endl;}
//如果剩余重量大于第二艘船的载重量,则无解
end=clock();
printf("The time is %6.3f",(double)(end-start-over)/CLK_TCK); //显示运行时间
cout<<endl;
system("pause");
return 0;
}
template<class Type>
void Loading<Type>::Backtrack(int i) //搜索第i层结点
{
if(i>n) //到达叶结点
{
if(cw>bestw)
{
for(int j=1;j<=n;j++)
{
bestx[j]=x[j]; //更新最优解
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];
}
template<class Type>
Type MaxLoading(Type w[],Type c,int n,int bestx[]) //返回最优载重量{
Loading<Type>X;
//初始化Loading类X
X.x=new int[n+1]; //动态分配
X.w=w;
X.c=c;
X.n=n;
X.bestx=bestx;
X.bestw=0;
X.cw=0;
//初始化r
X.r=0;
for(int i=1;i<=n;i++)
X.r+=w[i];
X.Backtrack(1); //调用回溯函数
delete []X.x;
return X.bestw; //返回最优解
}。