【计算机算法与设计 C++版 】回溯法


递归回溯

//P117递归回溯

//思想→不可运行
/* 
void Backtrack(int t)//t为解空间树的层数
{
	if(t>n)//递归出口,n为元素个数,若有n个元素,解空间树应有n层,所以若t>n说明已经找到解空间数中的一个死结点(子集)
		Output(x);
	else
	{
		for(int i=f(n,t);i<g(n,t);i++)//f(n,t)和g(n,t)分别表示在当前扩展结点处未搜索过的的子树的起始编号和终止编号,对于二叉树→左子树(0)和右子树(1)
		{
			x[t]=h(i);//x[]保存选入状态,两个可能取的值{0,1},h(i)表示在当前扩展结点处x[t]的第i个可选值
			if(Constriant(t)&&Bound(t))//若满足剪枝函数条件,继续对下一层(t+1)进行深度优先搜索
				Backtrack(t+1);
		}
	}
}
 */

//对于子集生成问题
//f(n,t)=0,g(n,t)=1,h(i)=1(选)/0(不选)
//子集要全部生成,不涉及剪枝问题,因此剪枝函数不需要

 /*
void Bt(int t)//子集生成
{
	if(t>n)
	{
		for(i=1;i<=n;i++)
			cout<<x[i];
		cout<<endl;
	}
	else
	{
		for(i=0;i<=1;i++)
		{
			x[t]=1-i;//先选入1,后不选0
			Bt(t+1);
		}
	}
}
 */

//简化和补充,形成可运行程序
#include <iostream.h>

class Set
{
public :
	int n;//元素个数
	int x[20];//应动态生成,为简化
	void Bt(int);
};

void Set::Bt(int t)
{
	int i;
	if(t>n)
	{
		for(i=1;i<=n;i++)//从第一层起
			cout<<x[i]<<", ";
		cout<<endl;
	}
	else
	{
		x[t]=1;Bt(t+1);
		x[t]=0;Bt(t+1);
	}
}

void main()
{
	Set k;
	k.n=3;
	k.Bt(1);//从第一层开始进行深度优先搜索
}

迭代回溯

//P117-118 迭代回溯
//思想

 /*
void IterativeBacktrack()
{
	int t=1;
	while(t>0)
	{
		if(f(n,t)<=g(n,t))
		{
			for(int i=f(n,t);i<=g(n,t);i++)//f(n,t)和g(n,t)返回结点状态→左子树,右子树,回溯;
				                           //所以引入数组p[t],(1)为0是左子树,应x[t]=1,改向p[t]=1;
										   //                  (2)为1是右子树,应x[t]=0,改向p[t]=2;
										   //                  (3)为2应回溯,改向p[t]=0,同层下一个活结点左子树
			{
				x[i]=h(i);
				if(Constraint(t)&&Bound(t))//生成全部叶结点,不需要剪枝函数
				{
					if(Solution(t))//判断在当前扩展结点处是否已得到问题的可行解,即是否已经得到解空间树死结点(对应一个子集),可以用(t==n)实现
						Output(x);
					else
						t++;
				}
			}
		}
		else
			t--;
	}
}

 */

#include <iostream.h>

class Set
{
public :
	int n;
	int x[20];
	int p[10];
	void IBt();
};

void Set::IBt()
{
	int i,t=1;
	while(t>0)
	{
		if(p[t]<2)//应下行
		{
			if(p[t]==0)
			{
				x[t]=1; p[t]=1;//准备下一次搜索这一层时,搜索右子树
			}
			else
			{
				x[t]=0; p[t]=2;//准备下一次搜索这一层时,回溯
			}
			if(t==n)
			{
				for(i=1;i<=n;i++)
					cout<<x[i];
				cout<<endl;
			}
			else
				t++;//保证深度优先,每进一次循环,深度加1
		}
		else//不能下行,需回溯
		{
			p[t]=0;//chw
			t--;
		}
	}
}

int main()
{
	Set k;
	k.n=2;//3;
	for(int i=0;i<10;i++)
		k.p[i]=0;
	k.IBt();
}

用回溯法求解背包问题

方案1:生成所有子集逐个检查

//用回溯法求解背包问题
//方案1:生成所有子集逐个检查

#include <iostream.h>

class knap
{
public :
	int n, x[20];
	int bx[20];//记最优解
	double best;//记最优值
	double c, w[20], v[20];
	void Bt1(int);
	double value();//计算价值
};

void knap::Bt1(int t)
{
	int i;
	double b;
	if(t>n)
	{
		b=value();//修改输出部分,计算本子集选择对应价值;若满足t>n条件,说明已经得到了一个解子集
			           //计算这个子集状态下的价值,以便进行比较,求得最优解
		if(b>best)//价值更好,就记录到bx[]和best;不好就放弃
		{
			best=b;
			for(i=1;i<=n;i++)
				bx[i]=x[i];
		}
	}
	else
	{
		x[t]=1;Bt1(t+1);//若没有得到一个子集,则先选入,并继续向下(下一层→t+1)进行深度优先搜索
		x[t]=0;Bt1(t+1);
	}
}

double knap::value()
{
	int i;
	double s1=0,s2=0;//s1记容量,s2记价值

	for(i=1;i<=n;i++)
		s1+=x[i]*w[i];//若被选入则x[i]=1,容量应被算入

	if(s1>c)
		return 0;//超过容量不能装,返回0
	for(i=1;i<=n;i++)
		s2+=x[i]*v[i];
	return s2;//若这个子集状态下装入不超过容量则计算价值并并返回

}

void main()
{
	double w[]={0,10,20,30};
	double v[]={0,60,100,120};
	int i;

	knap k;
	k.n=3;
	k.c=50;
	k.best=0;

	for(i=1;i<=k.n;i++)//为对象k的w[]和v[]成员赋值
	{
		k.w[i]=w[i];
		k.v[i]=v[i];
	}

	k.Bt1(1);
	
	cout<<"最优值为:"<<k.best<<endl;//输出最优值→价值最大
	cout<<"最优解为:";//每个物品的选入状态
	for(i=1;i<=k.n;i++)
		cout<<k.bx[i]<<", ";
	cout<<endl;	
}

方案2:先按单价排序…

//用回溯法求解背包问题
//方案2:先按单价排序,左子树表示容量允许再装入,右子树表示本次不装入,
//其余都装入能更好,并再深入
#include <iostream.h>
class knap
{
public :
	int n;
	double best, c, w[20], v[20];
	double cp, cw;//cp表示当前价值,cw表示当前容量
	void Bt2(int);
	double Bound(int);//计算能(尽可能装)获得最大价值
};

void knap::Bt2(int t)
{
	if(t>n)
		best=cp;//未处理x[],必要可加入
	else
	{
		if((cw+w[t])<=c)//左子树查容量,如果容量允许就再装入
		{
			cw+=w[t];//生成左子树,所以增加
			cp+=v[t];
			Bt2(t+1);//继续深入,此语句执行结束,则说明满足t>n,即找到一个子集
			cw-=w[t];//准备生成右子树,右子树是不装入,所以恢复到cw/cp没加这一层之前
			cp-=v[t];
		}
		if(Bound(t+1)>best)//注意参数为t+1,右子树,t层表示不装入,若能得到更好的最优值,则再深入
			Bt2(t+1);
	}
}

double knap::Bound(int s)//计算右子树中解的上界(可能取得的最大值)的方法时将剩余物品按单位价值递减排序,依次装入,直至装不下,在装入剩下物品的一部分装入背包
{
	double cleft=c-cw;
	double b=cp;	
	
	//方法1:
	while(s<n && w[s]<=cleft)//以单位价值递减顺序装入物品(贪心算法)
	{
		cleft-=w[s];
		b+=v[s];
		s++;
	}
	if(s<=n)
		if(cleft>=w[s])
			b+=v[s];
		else//
			b+=v[s]*cleft/w[s];	

	//方法2:
	/*while(s<=n && w[s]<=cleft)//以单位价值递减顺序装入物品(贪心算法)
	{
		cleft-=w[s];
		b+=v[s];
		s++;
	}
	if(s<=n)
		b+=v[s]*cleft/w[s];*/

	return b;
}

旅行售货员问题

//旅行售货员问题

#include <iostream.h>
//template <class T>
class perm
{
public:
	int n;//n个元素排列
	//T x[10];//存入x[]数组,
	char x[10];
	void Bt(int);
};

//template <class T>
//void perm<T>::Bt(int t)
void perm::Bt(int t)
{
	int i;
	char s;
	if(t==n)
	{
		for(i=1;i<=n;i++)
			cout<<x[i]<<", ";
		cout<<endl;
	}
	else
		for(i=t;i<=n;i++)
		{
			s=x[t];x[t]=x[i];x[i]=s;
			Bt(t+1);
			s=x[t];x[t]=x[i];x[i]=s;
		}	
}

void main()
{
	perm p;
	p.n=3;

	p.x[1]='A';
	p.x[2]='B';
	p.x[3]='C';

	p.Bt(1);

}

旅行售货员问题——方案1

//旅行售货员问题——方案1

#include <iostream.h>
class trav
{
public :
	int n,//元素个数
		best,//最优值
		x[10],//生成的所有排列
		bx[10],//记最优解
		a[10][10];//邻接矩阵,表示已知
	void Bt1(int);
	int value();//计算代价
};

int trav::value()
{
	int i,s=0;
	for(i=1;i<n;i++)
		s+=a[x[i]][x[i+1]];//计算每边代价累加,例:x=(1,3,2,4),但售货员要返回出发地,即还要计算4→1的代价
	s+=a[x[n]][x[1]];//最后边
	return s;
}

void trav::Bt1(int t)
{
	int i,s;
	if(t==n)
	{
		int b=value();
		if(b<best)
		{
			best=b;
			for(i=1;i<=n;i++)
				bx[i]=x[i];
		}
	
	}
	else
		for(i=t;i<=n;i++)//生成所有排列,x[]数组中存了n个值
		{
			s=x[t];x[t]=x[i];x[i]=s;
			Bt1(t+1);
			s=x[t];x[t]=x[i];x[i]=s;
		}
}

void main()
{
	int a[10][10]={
					{0,1,2,3,4},
					{1,99,30,6,4},
					{2,30,99,5,10},
					{3,6,5,99,20},
					{4,4,10,20,99}  };
	trav k;
	k.n=4;
	k.best=99;

	for(int i=1;i<=k.n;i++)
	{
		k.x[i]=i;
		for(int j=1;j<=k.n;j++)
			k.a[i][j]=a[i][j];
	}
	k.Bt1(2);//1作为起点,不参与生成排列
	
	cout<<k.best<<endl;
	for(int m=1;m<=k.n;m++)
		cout<<k.bx[m]<<", ";
	cout<<endl;
}

旅行售货员问题——方案2

//旅行售货员问题——方案2

#include <iostream.h>
class trav
{
public :
	int n,
		cc,//当前代价
		NoE,//无边标记,可用最大值99
		best,
		bx[10],
		x[10],
		a[10][10];
	void Bt2(int);
};

void trav::Bt2(int t)
{
	int i,s;
	if(t==n)
	{
		if(a[x[n-1]][x[n]]!=NoE//叶结点和之前结点是否有边
			&&a[x[n]][1]!=NoE//叶结点和之后结点是否有边→叶结点处检查前后两边
			&&((cc+a[x[n-1]][x[n]]+a[x[n]][1])<best)//在叶结点处,若都有边,算入代价,看是否小于best
			||best==NoE)//初始必求,&&优先级高于||
		{
			for(i=1;i<=n;i++)
				bx[i]=x[i];//找到最优解存进bx[]中

			best=cc+a[x[n-1]][x[n]]+a[x[n]][1];//找到最优值存best,并用于后面比较至最优		
		}		
	}
	else
	{
		for(i=t;i<=n;i++)
		{
			if(a[x[t-1]][x[i]]!=NoE//检查非叶结点,结点i前边有边
				&&(cc+a[x[t-1]][x[i]])<best//当前代价加入后,若小于best
				||best==NoE)//NoE是best的初值
			{
				//cc+=a[x[t-1]][x[i]];//method1
				s=x[t];x[t]=x[i];x[i]=s;
				cc+=a[x[t-1]][x[t]];//method2
				Bt2(t+1);
				cc-=a[x[t-1]][x[t]];//method2
				s=x[t];x[t]=x[i];x[i]=s;
				//cc-=a[x[t-1]][x[i]];//method1
			}
		}

	}
}

void main()
{
	int a[10][10]={
					{0,1,2,3,4},
					{1,99,30,6,4},
					{2,30,99,5,10},
					{3,6,5,99,20},
					{4,4,10,20,99}  };
	trav k;
	k.n=4;
	k.NoE=99;
	k.cc=0;
	k.best=999;

	for(int i=1;i<=k.n;i++)
	{
		k.x[i]=i;
		for(int j=1;j<=k.n;j++)
			k.a[i][j]=a[i][j];
	}
	k.Bt2(2);

	cout<<k.best<<endl;
	for(int m=1;m<=k.n;m++)
		cout<<k.bx[m]<<", ";
	cout<<endl;
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值