最短Hamilton路径(二进制&&状态压缩dp)

该博客介绍了如何利用二进制和状态压缩动态规划方法求解带权无向图中从0到n-1的最短Hamilton路径。题目保证了图的性质,并给出了数据范围和输入输出格式。博主分享了思路,强调了由于状态数量巨大,常规方法无法在规定时间内完成,因此采用了状态压缩技巧。通过二进制表示已访问的节点,将问题规模缩小到可行范围,并提供了状态转移方程和完整的代码实现。
摘要由CSDN通过智能技术生成

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

输入格式

第一行输入整数n。

接下来n行每行n个整数,其中第i行第j个整数表示点ii到jj的距离(记为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]。

输出格式

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

数据范围

1≤n≤20
0≤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

 思路:因为要遍历所有转态,而本题的状态太多(n最大为20,20个点组成的所有不同路径为20(的全排列)即20的阶乘种),无法在规定时间内完成,所以采用状态压缩,这里起点和终点是固定的,有些路径是无效的或可以去掉的(数值比较大的),所以只考虑当前哪些点走过和当前的位置在哪个点上(这里所谓的状态压缩就是用二进制的数表示状态数,二进制的每一位的数值0或1分别表示当前位的编号的点没走过和走过了,这样每一位都是0或1两种情况,一共20位,所以有2的20次方个数对应2的20次方个状态,再乘20个当前位置(终点),一共约2乘10的7次方,这样的数据范围就可以接受了),每次只由上一个对应状态(当前状态去掉当前点的状态)转移过来,计算当前最优:

状态转移: dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+w[k][j]);(表示当前i状态在点j时的最优解为当前最优解和由k转移到j的最优解(没有j这个点的状态的最优解+k到j的权值)两者中的min)

完整代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1<<20;
int n,dp[maxn][20],w[20][20];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>w[i][j];
        }
    }
    memset(dp,0x3f,sizeof(dp));//因为求最小值,所以初始化正无穷
    dp[1][0]=0;//一开始在起点0上,所以用二进制1表示当前第0位是1(即:0这个点走过了)
    for(int i=0;i<1<<n;i++){//遍历所有状态(所有二进制数)
        for(int j=0;j<n;j++){//遍历每个点(二进制的每一位)
            if(i>>j&1){//当前二进制数i右移j位(就是把i的第j位移到个位),然后与个位数字1亦或就可以知道当前状态的第j个点是否走过了
                for(int k=0;k<n;k++){遍历每个点(二进制的每一位),这次看j可以由那个点转移过来
                    if(i-(1<<j)>>k&1){//当前状态i减去2的j次方表示当前状态去掉j这个点的状态,j之前的状态,然后再按上面的方法(>>k&1),看第k位是否为1,就是是否能由k这个点转移过来
                        dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+w[k][j]);
                    }
                }
            }
        }
    }
    cout<<dp[(1<<n)-1][n-1]<<endl;//最后2的n次方-1(即最后一个状态数(二进制的每一位全为1表示每一位都走过)并且最终在n-1这个点(终点)的最优解即为答案
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值