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

注意,旅行商問題一般有一個默認的起點.

 

一.問題分析

 

1.狀態結點的數據類型:

int cl;     表示已走的路程

int id;   表示當前準備判斷的結點序號

int x[N];    表示一個最佳策略

 

2.約束條件:下一個結點和當前結點有通路,並且解 < 當前的最優解,並且終點和起點形成迴路

3.限界條件:當前狀態的最大價值上界 > 當前最優解

 

4.初始條件:進入隊列的第一個結點是數組元素[0],已走過的路程=0,下一步該判斷結點1:最佳路徑是默認值{0,1,2}

cl=0;   id=1;  x[N]={0,1,2,3,4,5....};

 

5.終止循環條件:

解向量序號 0 1 2 3 4 ... N-2 N-1
              id 1 2 3 4 5 ... N-1 N

在排列樹中,當遍歷到倒數第二層(N-2)時,該層結點的子節點只有1個 ,x[N]已完全確定

結點終止循環(約束)條件爲

id==N-1                                                                         //遍歷到倒數第二層

T[ livenode.x[ N-1 ]][ livenode.x[ N-2 ]] != \infty                 //倒數第二個和最後一個聯通

T[ 0 ][ livenode.x[ N-1 ] ] !=  \infty                                      //最後一個和第一個聯通

livenode.cl + T[ livenode.x[ N-1 ]][ livenode.x[ N-2 ]] + T[ 0 ][ livenode.x[ N-1 ] ] <bestp       //相加後小於目前最優值

 

 

6.活結點的限界條件:(最優值是最小值的題型,不用寫上界函數)

if( livenode.cl > bestp )

    continue;

 7.求全排列的題型中,必定要有swap()存在,將子問題的解從原處交換到隊首

 

 

二.代碼實現

1.全局變量區

#include"allh.h"
//給定起點

//全局變量
const int N3 = 4;				//城市數量
const int INF = INT_MAX;

vector<vector<int>>T3 ={		//鄰接矩陣
{INF,15,30,5},
{15,INF,6,12},
{30,6,INF,3},
{5,12,3,INF },
};

int bestp3;								//記錄最優值
vector<int>bestx3(N3,0);		//記錄最優解

//記錄狀態
struct State2
{
	int cl;			//記錄已走過的路程
	int id;		//記錄當前判斷的結點號

	vector<int> x;

	State2() {};
	State2(int _cl, int _id) {
		cl = _cl;
		id = _id;
		x.resize(N3);
	}

	//結構體組成優先隊列,需要寫優先函數,該函數不是struct的成員函數,只是作爲友元函數寫在結構體裏
	friend bool operator <(const State2& a, const State2& b)
	{
		return a.cl > b.cl;     //只看符號左邊:"怎樣的元素優先值更小呢?cl大的數"
	}
};

2.廣度遍歷函數

double TravelingBFS()
{
	//先壓入root結點,讓while()開動起來
	int t;		//記錄當前物品序號
	State2 livenode, newnode;
	newnode = State2(0, 1);
	for (size_t i = 0; i < N3; i++)
		newnode.x[i] = i;

	priority_queue<State2> q2;
	q2.push(newnode);

	//開始循環
	while (!q2.empty())
	{
		livenode = q2.top();
		q2.pop();
		t = livenode.id;

		//活結點的終止循環(約束)條件
		if (t == N3-1                                                         &&	   //到達倒數第二層
		T3[livenode.x[N3 - 2]][livenode.x[N3 - 1]]!=INF  &&      //倒數第二個城市和最後一個城市有連接
		T3[livenode.x[ 0 ]][livenode.x[N3 - 1]] != 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;		//接着循環
		}

		//活結點的限界條件
		if (livenode.cl >= bestp3)
			continue;

		//開始搜索子結點
		for (size_t i = t; i < N3; i++)
		{		
			int cl = livenode.cl + T3[livenode.x[t - 1]][livenode.x[i]];
			//livenode狀態下已處理的結點的序號=livenode.id-1
			if (T3[livenode.x[t - 1]][livenode.x[i]] != INF  && cl<bestp3				   												)		
			{
				newnode = State2(cl, livenode.id + 1);
				newnode.x = livenode.x;
				swap(newnode.x[t], newnode.x[i]);			//x[t]是子問題解向量的隊首,x[i]是可行解.交換後讓已知解空間+1
				q2.push(newnode);
			}
		}
	}
	return bestp3;
}

3.調用&打印

void TSP()
{
	//初始化
	bestp3 = INF;
	bestx3.resize(N3,0);
	//遍歷
	cout<<"最短距離爲:		"<<TravelingBFS();
	cout << endl;
	cout << "最優旅行順序:		";
	for (int i = 0; i <N3; i++)
	{
		cout << bestx3[i] << "-";
	}cout << "0";
}

 

三.Bug總結

1.按求解值,分支限界法可分爲MAX/MIN兩種.本題爲最小值的問題,不需要Bound()函數作爲限界條件.如果當前值比bestp更大,直接跳過.

2.按最優策略,分支限界法可分爲01型和序列型.本題屬於序列型,注意swap()的使用

 

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