Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

『最短Hamilton路径 状态压缩DP』

Parsnip·2019-02-09 19:47·347 次阅读

『最短Hamilton路径 状态压缩DP』

<更新提示>

<第一次更新>状压DP入门


<正文>

最短Hamilton路径#

Description#

给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

Input Format#

第一行一个整数n。 接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^7的正整数,记为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]。

Output Format#

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

Sample Input#

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

Sample Output#

Copy
4

解析#

很容易想到的朴素解法是枚举全排列,时间复杂度O(n!n),显然是会TLE的。注意到n<20,我们考虑状态压缩DP。设f[i][S]代表当前遍历状态为S,到了第i个点的最短长度。如何理解遍历状态S呢?我们把它当做一个二进制的01串,从右数第i位为如果为0,就说明节点i没有被遍历到过,如果第i位为1,则说明节点i被遍历到过了。我们将整个图的遍历状态记为一个二进制数,这就是状态压缩。

那么我们考虑如何DP。我们将编号记为从1开始的,那么初始状态就是f[1][1]=0
这里我们需要先理解状态的查询和赋值操作:

1.S&(1<<(i-1))代表取出状态S的(从右往左)第i位
2.S|(1<<(i-1))代表将状态S的(从右往左)第i位赋值为1

那么我们就可以得到状态转移方程了:f[j][S|(1<<(j1))]=min
需要满足节点j未访问,节点i已经访问过了。

状态的初值一开始均为正无穷,枚举S,i,j即可转移,时间复杂度O(n^2*2^n),目标状态为f[n][(1<<n)-1]

Code:

Copy
#include<bits/stdc++.h> using namespace std; const int N=20; int n,dis[N+5][N+5],f[N+5][(1<<N)+5],ans=0x3f3f3f3f; inline void input(void) { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&dis[i][j]); } inline void dp(void) { memset(f,0x3f,sizeof f); f[1][1]=0; for(int S=1;S<(1<<n);S++) for(int i=1;i<=n;i++) if(S&(1<<(i-1))) for(int j=1;j<=n;j++) if( not (S&(1<<(j-1))) ) f[j][S|(1<<(j-1))]=min(f[j][S|(1<<(j-1))],f[i][S]+dis[i][j]); } int main(void) { freopen("test.in","r",stdin); freopen("test.out","w",stdout); input(); dp(); printf("%d\n",f[n][(1<<n)-1]); }

<后记>

posted @   Parsnip  阅读(347)  评论(0编辑  收藏  举报
编辑推荐:
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
阅读排行:
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
点击右上角即可分享
微信分享提示
目录