最短Hamilton路径(状压DP)

描述

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

输入

第一行一个整数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]。

输出

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

样例输入

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

样例输出

4

思路:
刚开始看见n<=20直接爆搜了,结果轻敌了,TLE教我做人。粗略分析一下爆搜的复杂度为O(n^n)级别,不T才怪。emmmm……暴力不行那就换种思路,用DP的思路。
初始状态在0点,下一个状态有(n-1)种,去0以外任意一点,下一状态的随后的状态有(n-2)种……这样一张树形图就出现了。每一个状态可以描述为走了哪几个点,且最后到达的点是哪一个,因为n<=20,所以可以用二进制数很方便的描述出某一状态经过了哪些点,考虑最后到达的点这一信息,此状态又可以分成一些小状态,于是可以用dp[ i ][ j ]来描述,表示到达i状态的最后一步落在j点的最小距离。
根据题意可以知道从0点走到(n-1)点只要走n-1步就够了,即有n-1层,要构建每一层的状态,可以上一层的状态得到,好像有点bfs的意思。
动态转移方程为:dp[nex][j]=min(dp[nex][j],dp[x][k]+G[k+1][j+1]);

#include<stdio.h>
#include<algorithm>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
const int Ms=1<<19;
int scheme[2][Ms],dp[Ms][20],cnt[2];
bool vis[Ms];
int G[20][20];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            scanf("%d",&G[i][j]);
        }
    }
    memset(dp,0x3f,sizeof dp);
    int now=0,pre=1;
    for(int i=0;i<n;i++)
    {
        dp[1<<i][i]=G[0][i+1];
        scheme[now][cnt[now]++]=1<<i;
    }
    n--;
    for(int stp=1;stp<n;stp++)
    {
        now^=1;
        pre^=1;
        cnt[now]=0;
        for(int i=0;i<cnt[pre];i++)
        {
            int x=scheme[pre][i],nex;
            for(int k=0;k<n;k++)
            {
                if(((1<<k)&x)==0)continue;
                for(int j=0;j<n;j++)
                {
                    if((1<<j)&x)continue;
                    nex=x+(1<<j);
                    if(!vis[nex])
                    {
                        scheme[now][cnt[now]++]=nex;
                        vis[nex]=true;
                    }
                    //printf("%d -> %d = %d\n",k+1,j+1,dp[x][k]+G[k+1][j+1]);
                    dp[nex][j]=min(dp[nex][j],dp[x][k]+G[k+1][j+1]);
                }
            }
        }
    }
    printf("%d\n",dp[(1<<n)-1][n-1]);
    return 0;
}

若有什么错误,欢迎指正^ _ ^ 。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值