算法设计与分析:第四章 动态规划 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;
}

你可能感兴趣的:(动态规划,算法设计与分析)