1. 程式人生 > >演算法設計與分析:第四章 動態規劃 4.2TSP之貨郎擔問題

演算法設計與分析:第四章 動態規劃 4.2TSP之貨郎擔問題

/*
如果對於任意數目的n個城市,分別用1~n編
號,則這個問題歸結為在有向帶權圖中,尋找一
條路徑最短的哈密爾頓迴路問題。
這裡,V表示城市頂點,(i,j) ∈E 表示城市之
間的距離,用鄰接矩陣C表示城市之間的距離。


思想:
1設d(i,V-{i})表示從頂點i出發,經過V-{i}中各頂點一次,回到頂點i的最短路徑長度
2d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})}
3邊界:d(k,空集) = Cki,k!= i,從頂點k出發,不經過任何頂點,回到頂點i的長度,自然是Cki
4目標狀態:d(0,{1,2,3})

難點:如何標識這個集合V,難道用標記法,這是集合上的動態規劃問題,可以通過位來進行操作
0001表示第i個節點選中
那初始時:1111...1110
			n-1個1 1個0

輸入說明:0標識不可達
輸入:
4
0 3 6 7
5 0 2 3
6 4 0 2
3 7 5 0

4
0 8 5 6
6 0 8 5
7 9 0 5
9 7 8 0

輸出:
10
23

難點:
1到底遞迴求的是什麼?
分析即所得,求的是集合
求的是:d(i,V-{i}) = d(iStart,iFull - (1<<iStart) )
2遞迴傳入的引數是什麼?
iSart,初始節點,除去初始節點的剩餘節點集合,為一個數
3遞迴基是什麼?
遞迴基是:如果碰到已經求過的,則直接返回;碰到i != g_iStart && s == 0,返回g_dp[i][s] = g_iArr[i][g_iStart];
4如何挑選k?
	for(int k = 0 ; k < n ; k++)
	{
		//如果已經含有k,才挑選
		if(s & (1 << k))
		{
			//d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})}
			g_dp[k][s ^ (1<<k)]  = TSP(k,s ^ (1<<k),n);
*/

/*
關鍵:
1 //對於多個狀態,只能用遞迴來做。
2 	//記憶化搜尋
	if(g_dp[i][s] != -1)
	{
		return g_dp[i][s];
	}
	//遞迴基
	if(i != g_iStart && s == 0)
	{
		return g_dp[i][s] = g_iArr[i][g_iStart];
	}
3 	for(int k = 0 ; k < n ; k++)
	{
		//如果已經含有k,才挑選
		if(s & (1 << k))
		{
			//d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})}
			g_dp[k][s ^ (1<<k)]  = TSP(k,s ^ (1<<k),n);
			//選取最小值
			if(iMin > g_dp[k][s ^ (1<<k)] + g_iArr[i][k])
			{
				iMin = g_dp[k][s ^ (1<<k)] + g_iArr[i][k];i
			}

4 1設d(i,V-{i})表示從頂點i出發,經過V-{i}中各頂點一次,回到頂點i的最短路徑長度
2d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})}
3邊界:d(k,空集) = Cki,k!= i,從頂點k出發,不經過任何頂點,回到頂點i的長度,自然是Cki
4目標狀態:d(0,{1,2,3})
*/

#include <stdio.h>
#include <string.h>

const int MAXSIZE = 20;
int g_dp[MAXSIZE][1 << MAXSIZE];//採用集合的方式來做
int g_iArr[MAXSIZE][MAXSIZE];
int g_iStart = 0;


//對於多個狀態,只能用遞迴來做。
int TSP(int i,int s,int n)
{
	//記憶化搜尋
	if(g_dp[i][s] != -1)
	{
		return g_dp[i][s];
	}
	//遞迴基
	if(i != g_iStart && s == 0)
	{
		return g_dp[i][s] = g_iArr[i][g_iStart];
	}

	//開始進行遞推,從底向上
	int iMin = 1000000000;
	//選擇下一個城市
	for(int k = 0 ; k < n ; k++)
	{
		//如果已經含有k,才挑選
		if(s & (1 << k))
		{
			//d(i,V-{i}) = d(i,|V) = k屬於V min{Cik,d(i,!V - {k})}
			g_dp[k][s ^ (1<<k)]  = TSP(k,s ^ (1<<k),n);
			//選取最小值
			if(iMin > g_dp[k][s ^ (1<<k)] + g_iArr[i][k])
			{
				iMin = g_dp[k][s ^ (1<<k)] + g_iArr[i][k];
			}
		}
	}
	return g_dp[i][s] = iMin;
}

void process()
{
	int n;
	while(EOF != scanf("%d",&n))
	{
		if(n <= 0)
		{
			break;
		}
		memset(g_iArr,0,sizeof(g_iArr));
		for(int i = 0 ; i <= n - 1 ; i++)
		{
			for(int j = 0 ; j <= n - 1 ; j++)
			{
				scanf("%d",&g_iArr[i][j]);
			}
		}
		int iStart = 0;
		memset(g_dp,-1,sizeof(g_dp));
		//假設選定i=1作為初始節點
		//初始化動態規劃陣列
		for(int i = 0 ; i <= n-1 ; i++)
		{
			if(i != iStart)
			{
				g_dp[i][0] = g_iArr[i][iStart];
			}
		}
		TSP(iStart,(1<<n)-2,n);
		printf("%d\n",g_dp[iStart][(1<<n)-2] );
	}
}

int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}