• 最短Hamilton路径(状压dp)


    最短Hamilton路径实际上就是状压dp,而且这是一道作为一个初学状压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

    思路讲解

    作为一道最基础的状压dp ,我们需要掌握它为什么是这么做的。

    我作为一名菜鸡,首先想到的就是朴素做法,可是朴素做法它的时间复杂度不允许我通过这道题目。  

    那我们分析一下朴素做法,从起点到终点每个点只经过一次且求最短路径,,,嗯,最暴力的话就是我们应该把所有的不同种路径全都枚举出来(这个当然就是全排列啦),然后去比较寻找最短路径。

    那么这个复杂度是O(n*n !),因为我们枚举所有情况是O(n!),然后每一种路径求和是O(n)的,所以总复杂度是O(n*n!),这个不难分析。

    但是我们想了,这么大的复杂度该怎么办呢?  我们再来想想 ,我们的复杂度之所以大是因为  “  n!  ” ,所以我们试图从这里想想办法。

    枚举每一位,所有种方案,,,我们可以用二进制,因为二进制同样可以把一组数表示出来,,所以我们想到了用状压去做。

    我们定义f(i,j) i 表示的是当前的二进制数  , j 表示的是当前所到达的二进制的第j位  

    在任意时刻,我们还需要知道当前所处的位置,因此我们用f(i ,j )表示“点被经过的状态” 对应的二进制数位i 且目前处于j时的最短路径。

    在任意时刻,有公式f【i,j】=min(i xor (1<<j)  ,    k )+a(k,j)     a数组表示从k到j的路径大小。

    i xor  (1<<j ) 表示的是在上一时刻我们所处位置是的路径和

    所以公式的意思就明白了  ,就是上一个时刻的路径和与当前时刻的路径和大小的比较

    代码实现

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int n ;
    int a[25][25];   // 代表i到j路径花费 
    int f[1<<22][25];   //利用二进制的思想 , i表示的是当前的二进制数, j表示的是当前所到达的二进制的第j位 
    int main(){
        scanf("%d",&n);
        for(int i=0 ;i<n;i++){
           for(int j=0 ;j<n;j++){
               scanf("%d",&a[i][j]);
           }    
         }
       
        memset(f,127,sizeof(f));  
        f[1][0] = 0 ;
        for(int i=1 ;i<(1<<n);i++){
            for(int j=0 ; j<n ;j++){
                if((i>>j)&1){     //表示的是我们枚举二进制数的时候  ,这个数中第j位是不是已经被选中了,如果没选过,那我们还用它干嘛。最后的结果不就是n-1个数全部选中嘛
                    for(int k=0 ;k<n ;k++ ){
                        if((i>>k)&1){
                            f[i][j] = min(f[i][j], f[i^(1<<j)][k]+a[k][j]) ;
                        }
                    }
                }
            }
        } 
        cout << f[(1<<n)-1][n-1]<<endl;
        return 0 ;
    }
  • 相关阅读:
    exFAT移动硬盘写保护怎么去掉
    ORACLE:一列的多行数据拼成字符串
    cxgrid中,如何根据列名或字段名取得footer值
    Delphi天气预报查询
    Delphi ListView基本用法大全
    datasnap的初步
    Delphi ListView基本用法大全
    Delphi实现树型结构
    获取身份证号码信息
    让Delphi XE5跟其他版本的Delphi共存
  • 原文地址:https://www.cnblogs.com/xiaoK-778697828/p/10152692.html
Copyright © 2020-2023  润新知