`
hulianwang2014
  • 浏览: 681208 次
文章分类
社区版块
存档分类
最新评论
  • bcworld: 排版成这样,一点看的欲望都没有了
    jfinal

广义货郎担架问题(TSP,广义哈密顿环问题)---允许有向图

 
阅读更多
著名的货郎担架问题大家都明白,现在要求解它。有两种办法
方法一,暴力枚举法,举出所有的路径,这方法最简单,但是,需要N!的复杂度,当n比较大时,完全没有可计算性,当然,生成n!种排列比较简单,不需要什么高端的技巧。在此不解释这种解法

方法二,动态规划,设T(Vi,V)表示从V1经过V中所有结点到Vi的最短路径值,于是我们有以下的转移方程

T(Vi,V)=min{D(k,i)+T(Vk,V\{Vk}}其中Vk是V中元素,其中D(k,i)表示第k个结点到第i个的距离(允许取无穷大)。我们要求解的问题是T(V1,V\{V1}).----即我们从V1出了,遍历V中除V1之外的结点,然后回到V1.根据这个原则,我们用二进制位表示每一个元素的取与不取。则知道代码如下:

//author:sy1206321
#include <iostream>
using std::endl;
using std::cin;
using std::cout;

unsigned	int		iNodeCount;//结点的个数
unsigned	int**	lpDpArray;//动态规划时的数组
unsigned	int**	lpGraph;//存储图的数据
bool		**		bHasCaculate;//存储一条路径是否访问过
unsigned	int**	lpPath;//存储最短的路径

bool	FreeResource();//free memory
bool	InitData();//init graph data
void	Search();//搜索最短的路径
unsigned	int	GetMinPath(unsigned	int iNode, unsigned int	iNodeSet);
void	ShowPath();//显示其中一个路径

int main(){
	InitData();
	Search();
	cout<<lpDpArray[1][(1<<iNodeCount)-2]<<endl;
	ShowPath();
	FreeResource();
	system("PAUSE");
}

bool	FreeResource(){//free memory
	for(int iIndex= 0; iIndex<= iNodeCount; ++iIndex){
		delete[]	lpDpArray[iIndex];
		delete[]	bHasCaculate[iIndex];
		delete[]	lpGraph[iIndex];
		delete[]	lpPath[iIndex];
	}
	delete[]	lpDpArray;
	delete[]	bHasCaculate;
	delete[]	lpGraph;
	delete[]	lpPath;
	return	true;
}

bool	InitData(){//allocate memory and init data
	cout<<"请输入结点个数:"<<endl;
	cin>>iNodeCount;
	lpDpArray		=	new unsigned int*[iNodeCount+ 1];
	lpDpArray[0]	=	NULL;
	bHasCaculate	=	new	bool*[iNodeCount+ 1];
	bHasCaculate[0]=	NULL;
	lpGraph			=	new	unsigned int*[iNodeCount+ 1];
	lpGraph[0]		=	NULL;
	lpPath			=	new	unsigned int*[iNodeCount+ 1];
	lpPath[0]		=	NULL;
	for(int iIndex= 1; iIndex<= iNodeCount; ++iIndex){
		lpDpArray[iIndex]		=	new	unsigned int[1<<iNodeCount];
		bHasCaculate[iIndex]	=	new	bool[1<<iNodeCount];
		lpGraph[iIndex]		=	new	unsigned int[iNodeCount+1];
		lpPath[iIndex]			=	new	unsigned int[1<<iNodeCount];
	}
	cout<<"请输入图的数据,如果不存在就输入0"<<endl;
	//读入图的数据,不存在则用无穷表示(static_cast<int>(-1))
	for(unsigned int iRow= 1; iRow<= iNodeCount; ++iRow){
		for(unsigned int iCol= 1; iCol<= iNodeCount; ++iCol){
			cin>>lpGraph[iRow][iCol];
			if(!lpGraph[iRow][iCol]){
				lpGraph[iRow][iCol]	=	static_cast<unsigned int>(-1);
			}
		}
	}
	//把bHasCaculate, lpDpArray, lpPath数组全部清零
	for(unsigned int iRow=1; iRow<= iNodeCount; ++iRow){
		for(unsigned int iCol= 1; iCol< (1<<iNodeCount); ++iCol){
			bHasCaculate[iRow][iCol]	=	false;
			lpDpArray[iRow][iCol]		=	static_cast<unsigned int>(-1);
			lpPath	[iRow][iCol]		=	0;
		}
	}

	return	true;
}

//=========================================================================
//lpDpArray[iNode][iNodeSet]表示从Node(1)到Node(iNode)经过iNodeSet集合中的点的
//最小值
//=========================================================================
void	Search(){
	//显然当iNodeSet为0是,表示空集
	for(int iNode= 1; iNode<= iNodeCount; ++iNode){
		lpDpArray[iNode][0]		=	lpGraph[1][iNode];
		lpPath	  [iNode][0]	=	1;
		bHasCaculate[iNode][0]	=	true;
	}
	lpDpArray[1][(1<<iNodeCount)-2]	=	GetMinPath(1, (1<<iNodeCount)-2);
}

unsigned	int	GetMinPath(unsigned	int iNode, unsigned int	iNodeSet){
	if(bHasCaculate[iNode][iNodeSet]){
		return	lpDpArray[iNode][iNodeSet];
	}
	unsigned int iMinValue	=	static_cast<int>(-1);
	for(int iPreNode= 1; iPreNode<= iNodeCount; ++iPreNode){
		if(((1<<(iPreNode-1)) & iNodeSet) && (lpGraph[iPreNode][iNode]!= -1)){//iPreNode is a elem of iNodeSet
			unsigned int iPreValue	=	GetMinPath(iPreNode, iNodeSet&(~(1<<(iPreNode-1))));
			if((iPreValue!= -1) && iPreValue+ lpGraph[iPreNode][iNode]< iMinValue){//update value
				lpPath[iNode][iNodeSet]		=	iPreNode;
				iMinValue	=	iPreValue+ lpGraph[iPreNode][iNode];
			}
		}
	}
	lpDpArray[iNode][iNodeSet]	=	iMinValue;
	bHasCaculate[iNode][iNodeSet]		=	true;
	return	lpDpArray[iNode][iNodeSet];
}

void	ShowPath(){
	int	*	path=	new	int[iNodeCount+ 1];
	int	ivalue=	(1<<iNodeCount)-1;
	int	iPreNode	=	1;
	int	iNodeIndex	=	0;
	while(ivalue){
		ivalue-=	(1<<(iPreNode-1));
		path[iNodeCount-iNodeIndex]	=	iPreNode;
		++iNodeIndex;
		iPreNode	=	lpPath[iPreNode][ivalue];
	}
	path[0]=	1;
	for(iNodeIndex= 0; iNodeIndex<= iNodeCount; ++iNodeIndex){
		cout<<path[iNodeIndex]<<(iNodeIndex== iNodeCount ? "\n":"->");
	}
	delete[]	path;
}

在其中我们并没有完全采用动态规划,而是采用它的一个变种---备忘录,我们把已经求得的还会重新利用的小的结果记录起来,下次再求解这个小问题的时候直接查表就可以了,而不是像动态规划那样,在求解一个大问题的时候要求所有的小问题都已经被解决。


易求算法时间复杂度是O(n*2^n)空间也是,显然在时间上比n!要快许多,而对于n!我们由斯特林公式知n!=(n/e)^n*(2*pi*n)^0.5,显然比2^n*n要大许多的


其中我给的一组输入值是:

6(6个顶点)

0 10 20 30 40 50
12 0 18 30 25 21
23 19 0 5 10 15
34 32 4 0 8 16
45 27 11 10 0 18
56 22 16 20 12 0

以上的是边的权值(有向图)

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics