最短Hamilton路径

题目

题目链接

题解

状压dp。


f[i][j]表示从0点按照路径i走到j点的最短距离。其中i为二进制数,1表示走过某点,0表示未走过某点,比如10010表示经过了1、4两个点,而不经过0、2、3点。

状态转移为:假设沿路径i走到j点经过k点,且由k直接到j,那么由于kj的距离是固定的,所以想要0j的距离最短,只要保证0k的距离最短即可,求出0k的距离加上kj的距离,取其中最小者就是0j的最短距离。


说实话,不明白为什么状态从00000每次加一遍历到11111就是正确的?如何(不严谨地)证明其合理性?很显然,如果将遍历的方式改为从11111每次减一到00000,那么结果就是错误的。(加一顺序遍历得到的每一个状态都可以由之前算得的状态计算出来)

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 20;
int n;
int w[N][N];
int f[1<<N][N]; 
int main()
{
	cin>>n;
	for(int i = 0;i < n;i ++)	
		for(int j = 0;j < n;j ++)
			cin>>w[i][j];
			
	memset(f, 0x3f, sizeof f);
	f[1][0] = 0; // 从0点走到0点的状态为(000001),距离为0
	for(int i = 0;i < (1 << n);i ++)
		for(int j = 0;j < n;j ++) // 枚举目的地,即最后一个点
			if((i >> j) & 1) // j点必须要走过才行
				for(int k = 0;k < n;k ++) // 枚举倒数第二个点
					if(((i - (1 << j)) >> k) & 1) // 除去j点后,k点必须走过才行
						f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
	
	cout << f[(1 << n) - 1][n-1] << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值