算法分析 | 分支限界法 | (優先隊列)優化後的旅行商問題

順便複習了類的繼承,派生類的寫法如下:

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()出隊已有的元素.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章