最短Hamilton路径

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

输入格式

第一行输入整数nn。

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

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

输出格式

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

数据范围

1≤n≤201≤n≤20
0≤a[i,j]≤1070≤a[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
#include<iostream>
#include<cstring>
using namespace std;
const int INF = 0x3f;

const int N=20,M=1<<20;
int f[M][N],weight[N][N];
//哈密顿图最短路径:无向有权图,从0号点走到n-1号点,不走重复路径且总距离最小
//解法:动态规划  确定状态与状态转移方程 初始状态
//状态:f[i][j] 以j号点结尾的 包含i个点的 最短距离
//用i的二进制表示当前所走过的点,j表示当前以j号节点结尾
//f[5][2]=15   5=101 表示当前走过的节点为0 2 且以2号节点为当前的最后一个节点 的最优距离为 15
//i = 1100110 则表示当前已经走过的点为 1 2 5 6号点,如果要去掉5号点:i-(1<<5) = 1000110
//状态转移方程: f[i][j]=min(f[i][j],f[i-(1<<j)][k]+weight[k][j]
//即 要求 以j号点为结尾的i个点的最短距离 = 于以j的前一个k点为结尾的i-1(去掉j这个点)的最短距离
//j的前一个节点k可能存在多个,所以要用for循环
int main(){
    
    int n;
    cin>>n;
    
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>weight[i][j];//i到j的边的权重
    
    memset(f,INF,sizeof(f));//0x表示16进制 3f最值
    f[1][0]=0;
    for(int i=0;i<(1<<n);i++){//从0-1<<n 所有点的所有包含情况 
                              //1个 2个 3个点的情况
        for(int j=0;j<n;j++){ //在包含i个点对应情况下的j结尾
            
            if( (i>>j) & 1)//只有当前的i中包含了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]+weight[k][j]);
            }
        }
    }
    
    
    cout<<f[1<<20-1][n-1]<<endl;
    
    return 0;    
}

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值