算法分析 | 分支限界法 | (优先队列)优化后的旅行商问题

顺便复习了类的继承,派生类的写法如下:

Derive (int _cl, int _rl, int _zl, int _id):Base(_cl, _id),rl(_rl),zl(_zl){}

注意,

1.基类的构造函数Base() 的两个参数是实参,Derive的形参传递给Base(),就是实参.如果写成Base(3,5),也不出错.

2.基类构造函数的调用放在函数头部,不能放在函数体{}中。

 


 

一.问题描述

优先队列中,zl=cl+rl作为优先级

cl:当前走过的路程.

rl:剩余的路径长度  拥剩余每个结点的最小出边之和.          例如有一表示1,2,3,4 四个城市的矩阵:

{INF,15,30,5},          min=5
{15,INF,6,12},          min=6
{30,6,INF,3},            min=3
{5,12,3,INF }            min=3

 初始时,剩余4个城市      rl=5+6+3+3=17   (注意,不同结点的最小出度可能相同)

 

二.代码实现

1.全局变量区,有一部分继承自TCP.h,(详见文章上一篇)

#include"TSP.h"	//继承全局变量和结构体State2

int minsum;		//记录剩余节点的最小出路之和
int minout[N3];	//记录各个结点的最小出路

//记录状态
struct State3: public State2
{
	int rl;
	int zl;

	State3() {};
	State3(int _cl, int _rl, int _zl, int _id):State2(_cl, _id),rl(_rl),zl(_zl){}

	//zl越小的,优先级越大
	friend bool operator<(const State3& a, const State3& b)
	{
		return a.zl > b.zl;
	}
};

 

2.Bound()下界函数,作用有二:

    (1)当某个结点和其它所有结点都不连通时,判定为false

    (2)记录各结点的最小出度 和 所有结点的最小出度之和.记录在minout[] 和minsum.

    问题在于,优先级zl = cl + rl.   cl 还好理解,   rl 的初始值=minsum; 每拓展出一个结点,rl -= minout[ i ]   

    另外,活结点的约束条件是   if(zl<bestp)

    最小出度之和为什么能决定优先级和限界条件?

    网上找不到对这个下界函数的解释.如果你有想法请评论.

bool Bound() {
	for (size_t i = 0; i <N3; i++)
	{
		int minl = INF;
		for (size_t j = 0; j < N3; j++)
		{
			if (T3[i][j] != INF && T3[i][j] < minl)
			{
				minl = T3[i][j];
			}
		}
		if (minl==INF)//未找到联通路径
		{
			return false;
		}
		minout[i] = minl;
		minsum += minout[i];
	}
	return true;
}

 

3.遍历函数

第一次运行时忘了加pop()弹出队首元素

 

double TravelingBFS_2()
{
	if (!Bound())
	{
		cout << "无可行解" << endl;
		return -1;
	}
	//循环前的初始化准备
	State3 livenode, newnode;
	newnode = State3(0, minsum, minsum, 1);	//minsum已在之前调用的Bound()中算出
	for (size_t i = 0; i < N3; i++)
		newnode.x[i] = i;

	priority_queue<State3> q;
	q.push(newnode);

	//开始循环
	while (!q.empty())
	{
		livenode = q.top();
		q.pop();
		int t = livenode.id;
		
		//活结点的终止循环(约束)条件
		if (t == N3 - 1)														//到达倒数第二个结点
		{
			if (T3[livenode.x[0]][livenode.x[N3 - 1]] != INF &&      //最后一个城市和起点城市有连接
				T3[livenode.x[N3 - 1]][livenode.x[N3 - 2]] != INF &&		//倒数第二个城市和最后一个城市有连接
				livenode.cl + T3[livenode.x[N3 - 2]][livenode.x[N3 - 1]] + T3[livenode.x[0]][livenode.x[N3 - 1]] < bestp3
				//相加后 < 目前最优值
				)
			{
				bestp3 = livenode.cl + T3[livenode.x[N3 - 2]][livenode.x[N3 - 1]] + T3[livenode.x[0]][livenode.x[N3 - 1]];//更新
				bestx3 = livenode.x;
			}
			continue;		//到达倒二结点已经求出一个bestx[],或已知不存在最佳顺序,必须跳出本次循环
		}

		//活结点的限界条件
		if (livenode.cl >= bestp3)
			continue;

		//开始遍历分支结点
		for (size_t i = t; i < N3; i++)
		{
			if (T3[livenode.x[t-1]][livenode.x[i]]!=INF)//约束条件:当前结点和下一个待测节点有链接
			{
				int cl = livenode.cl + T3[livenode.x[t - 1]][livenode.x[i]];
				int rl = livenode.rl - minout[livenode.x[i]];
				int zl = cl + rl;
				if (zl < bestp3)
				{
					newnode = State3(cl, rl, zl, t + 1);
					newnode.x = livenode.x;
					swap(newnode.x[t], newnode.x[i]);//t来自livenode.id,是livenode状态要处理的下一个结点序号,显然要换成i
					q.push(newnode);
				}
			}
		}
	}
	return bestp3;
}

 

4.初始化&调用函数

void TSP_2() {
	//初始化
	bestp3 = INF;
	bestx3.resize(N3, 0);
	minsum = 0;

	//遍历
	cout << "最短距离为:		" << TravelingBFS_2();
	cout << endl;
	cout << "最优旅行顺序:		";
	for (int i = 0; i < N3; i++)
	{
		cout << bestx3[i] << "-";
	}cout << "0";
}

三.Bug分析

1.在源.cpp调用TSP_2(),什么显示都没有,连距离为    "字样都没打印出来.

而且无法选择其它选项,说明程序陷入死循环,无法结束运行 <- 优先队列一直不为空 <- 忘记加pop()出队已有的元素.

 

posted @ 2020-03-07 22:10  心碎人俱乐部  阅读(139)  评论(0)    收藏  举报