最短Hamilton路径(状压DP)

给定一张 nn 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。

Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数 n。

接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i 到 j 的距离(记为 a[i,j])。

对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z]a[x,y]+a[y,z]≥a[x,z]。

输出格式

输出一个整数,表示最短 Hamilton 路径的长度。

数据范围

1≤n≤20
0≤a[i,j]≤10^7

输入样例:

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

输出样例:

18

样例说明:
从0到3的Hamilton路径有两条,0-1-2-3和0-2-1-3。前者的长度为2+2+1=5,后者的长度为1+2+1=4。

思路:

状态压缩动态规划
f[i][j] 表示以j号点结尾的 经过i中表示的所有点(用i的二进制形式表示当前所走过的点,0表示未经过,1表示经过,从二进制的低位到高位依次对应图中的0~n-1号点)的最短距离
如:f[5][2]=15   5=101 表示当前走过的节点为0 2 且以2号节点为当前的最后一个节点 的最短距离为 15;i = 1100110 则表示当前已经走过的点为 1 2 5 6号点,如果要去掉5号点:i-(1<<5) = 1000110。
 

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 20,M=1<<20;//1<<20表示1*2^20因为有n个点就要n位的二进制位来表示状态,又每个二进制位有 0 1 两个状态,所以总状态数有2*2*2...*2共2^20种状态
int n;
int f[M][N],weight[N][N];
signed main()
{
	cin>>n;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			cin>>weight[i][j];//存两点间的距离(即边的权值)
    memset(f,0x3f,sizeof f);//由于f[][]后面要不断更新得最小值,所以这里先初始化为无穷大的值
    f[1][0]=0;//1的二进制表示为0001(这里以四位二进制位为例)对应经过的点为0,故f[1][0]表示点0->0距离为0
    for(int i=0;i< 1<<n;i++)//遍历所有经过的点的情况
    {
    	for(int j=0;j<n;j++)//从以0为结尾点的最短路径更新到以n-1为结尾点的最短路径
    	{
    		if(i>>j&1)//如果i的二进制形式的第j位(从左往右数)为1,即经过了该位对应的节点
    		{
    			for(int k=0;k<n;k++)//表示从k节点到j节点,因为能到j的节点可能不只一个,所以要遍历一遍k节点
    			{
    				if(i-(1<<j)>>k&1)//表示i中没有经过j节点但是经过k节点的情况下
    					f[i][j]=min(f[i][j],f[i-(1<<j)][k]+weight[k][j]);
				}
			}
		}
	}
	cout<<f[(1<<n)-1][n-1]<<endl;//(1<<n)-1表示二进制位全为1即经过了所有的点
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_61725823/article/details/125587423