最短Hamilton路径

题意: 给定一个n个点的无向图,从0~n-1标记,求0到终点n-1的最短Hamilton路径。

解析: 有n个点,(n*(n-1))/2条边,求从起点0到终点n-1的距离最短的路径。

输入

4
0 1 3 2
1 0 4 3
3 4 0 3
2 3 3 0

输出

8

分析

  • 用朴素算法将所有可能的情况排列组合,再按照先后顺序计算路径求出最短路径,时间复杂度是(n*n!)。

  • 重新分析一波,先确定0点为起始点,再来计算其他的点,如果现在的状态是0 1 2 3四个点(这只是假设),如果此时处于3这个点,那么意味着从0经过了两个点到达了3,那么3之前的点是哪一个呢,就只可能是1和2,假设3之前是1这个点,那么路径就是0 2 1 3,如果我们知道了路径再回头去算最短路径,就会浪费很多时间,所以我们可以将之前经过的点的最短距离计算出来,储存在一个变量内,要用的时候就直接调用

  • 那我们怎么知道这个时候的该调用的变量是哪一个呢?就要用到一个n位二进制数(例如数字3二进制表示为11,所以这个可以看作0和1位的位置已经被走过了),用F[i,j]表示此时状态是i(这就是那个二进制数),且目前处于点 j (j 是几那么 i 的从右到左第几位就是1)的最短路径,所以之前的状态就是状态 i 去掉 j 这个节点的 状态 p,p=1<<j xor i(将j位置上的1去掉),此时p就是到达j之前的状态,但是p上可能也有很多1,所以我们要遍历上面的所有的1,就有了公式F[i,j] = min{F[1<<j xor i][k]+dis[k][j]} 那么结果就是F[1<<n-1][n-1 ] ( k就是p上可能的1,F[i,j]最短距离就是从 i 到 k 加上 k 到j的距离,所以 k从0到n-1循环)。

  • 那我们知道了公式,要怎么确定怎么寻欢遍历呢,我们看最后的操作是什么,是找到状态p上面的1,也就是找到k,所以k循环就是再在里面,所以这一层还有一个判断,**if(p<<k&1==1)(状态p上的k位置为1)**才能进行公式运算。然后再往上看是什么操作,他是一个在状态i上去除j节点的操作,j可以是i中很多个节点中的其中一个,所以这层就是j的循环,这层也需要一个判断,if(i<<j&1 == 1)(状态i上j位置为1).

全部代码

#include<iostream>
#include<cstring>
using namespace std;
const int N = 25;
int n,val[N][N],F[1<<20][N];

int main()
{
	cin>>n;
	memset(val,0,sizeof(val));
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			cin>>val[i][j];
	memset(F,0x3f,sizeof(F));
	F[1][0] = 0;
	for(int i=3;i<(1<<n);i++)//i可以从3开始,因为每次都是第0个先确定 
		if(i&1)
			for(int j=1;j<n;j++)//j可以从第一个开始 
				if((i>>j)&1)
					for(int k=0;k<n;k++)//k从第0个开始 
						if((1<<j^i)>>k&1)
							F[i][j] = min(F[i][j],F[1<<j^i][k]+val[k][j]);
	cout<<F[(1<<n)-1][n-1]<<endl;
	
	
	return 0;
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值