最短Hamilton路径题解

3 篇文章 0 订阅
3 篇文章 0 订阅

最短Hamilton路径

题目描述

给定一张 n 个点的带权无向图,点从 0 → n − 1 0\to n-1 0n1 标号,求起点 0 0 0 到终点 n − 1 n-1 n1 的最短 H a m i l t o n Hamilton Hamilton路径。 H a m i l t o n Hamilton Hamilton路径的定义是从 0 0 0 n − 1 n-1 n1不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数 n n n

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

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

输出格式

输出一个整数,表示最短 H a m i l t o n Hamilton Hamilton路径的长度。

数据范围

1 ≤ n ≤ 20 1≤n≤20 1n20
0 ≤ a [ i , j ] ≤ 107 0≤a[i,j]≤107 0a[i,j]107

输入样例:
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->1->2->3->4
0->1->2->4->3
0->1->3->2->4
...

​ 时间复杂度 O ( n × n ! ) O(n\times n!) O(n×n!),当n取最大值20时,时间复杂度是 O ( 2   432   902   008   176   640   000 ) O(2~432~902~008~176~640~000) O(2 432 902 008 176 640 000),和LLNG_MAX是一个数量级,一定超时.

优化的做法

​ 因为每个节点都有两种状态:访问过和没访问过,所以我们考虑:

​ 用状态压缩DP,将状态定位一个二进制数。

变量规定
F[i, j] = k		表示经过状态为i(i是二进制数), 且当前位置为j时,最优答案为k
w[x, y]			表示x到y的权值
则状态转移方程为

F [ i , j ] = m i n ( F [ k , j ] ,   F [ i   X o r   ( 1   L s h   j ) ,   k ] ) + w [ k , j ] F[i, j] = min(\color{red}{F[k, j]},~\color{blue}{F[i~Xor~ (1~Lsh~j),~k]}\color{black}) + w[k, j] F[i,j]=min(F[k,j], F[i Xor (1 Lsh j), k])+w[k,j]

(其中i的第j位为1,k为节点)

其中 F [ i   X o r   ( 1   L s h   j ) ,   k ] \color{blue}F[i~Xor~ (1~Lsh~j),~k] F[i Xor (1 Lsh j), k]为把 i i i的第 j j j位取反,即

j j j未经过,则标记为“此时经过”

j j j已经过,则标记为“取消经过”(方程中无此情况).

代码
首次提交,MlE
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;
const int N = 23;

int F[1 << N][N], n, w[N][N];

int dp(int n)
{
	F[1][0] = 0;
	for(int i = 1; i < (1 << n); ++i)
		for(int j = 0; j < n; ++j)
			if((i >> j) & 1)//the jth digit of the binary number i is 1
			    for(int k = 0; k < n; ++k)
			    	if((i >> k) & 1)
			    		F[i][j] = min(F[i][j], F[i ^ (1 << j)][k] + w[k][j]);
	return F[(1 << n) - 1][n - 1];
}
int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    	for(int j = 0; j < n; ++j)
    		scanf("%d", &w[i][j]);
    memset(F, 0x3f, sizeof(F));
    printf("%d", dp(n));
    return 0;
}
稍作改动,AC
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;
const int N = 20;

int F[(1 << N) + 1][N + 1], n, w[N][N];

int dp(int n)
{
	F[1][0] = 0;
	for(int i = 1; i < (1 << n); ++i)
		for(int j = 0; j < n; ++j)
			if((i >> j) & 1)//the jth digit of the binary number i is 1
			    for(int k = 0; k < n; ++k)
			    	if((i >> k) & 1)
			    		F[i][j] = min(F[i][j], F[i ^ (1 << j)][k] + w[k][j]);
	return F[(1 << n) - 1][n - 1];
}
int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    	for(int j = 0; j < n; ++j)
    		scanf("%d", &w[i][j]);
    memset(F, 0x3f, sizeof(F));
    printf("%d", dp(n));
    return 0;
}
说明!!!
//First
const int N = 23;

int F[1 << N][N], n, w[N][N];
//Second
const int N = 20;

int F[(1 << N) + 1][N + 1], n, w[N][N];

这两种申请空间方式中,F数组的空间差很多。

提醒大家,开变量需谨慎,勿让指数爆炸!

完结,撒花

_推荐绘图网站,这不是广告

在该网站输入:

0 0 0
0 1 2
0 2 4
0 3 5
0 4 1
1 0 2
1 1 0
1 2 6
1 3 5
1 4 3
2 0 4
2 1 6
2 2 0
2 3 8
2 4 3
3 0 5
3 1 5
3 2 8
3 3 0
3 4 5
4 0 1
4 1 3
4 2 3
4 3 5
4 4 0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值