• TSP问题之状压dp法


    首先,我们先来认识一下什么叫做TSP问题

    旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。假设这个n很小,我们就可以使用状态压缩的方法求解,在一般的TSP问题中的用状压求解的题目,我们可以定义一个dp数组,dp[i][v],其中v表示一个集合,dp[i][v]表示到i这个点经过v中所有点的最小路径.

    假设我们从s出发,最后再回到s

    1.那么最开始,只有dp[s][{s}]=0,其余均等于inf

    2.其他情况下,dp[i][state]=min(dp[i][state],dp[j][state']+c[j][i])

    3.最后我们的结果,ans=min(ans,dp[i][state]+c[i][s]),因为我们要求的是一个环的最短路,所以还要加上回来的距离

    那么还有一个问题,我们要如何存下这个集合,当然是用状态压缩的方法,s|1<<(k),表示由原来的状态s转移到加上k这个点的状态,那么就很好求解了对吧

    POJ3311

    题目大意:多组数据,给定n,一个起点0,以及这n+1个点之间的距离,求从起点出发经过每个点一次,再回到起点的最短距离.注意到n<=10,我们可以使用状压dp来做

    思路:首先先预处理出这n+1个点之间的最短距离,因为n很小,我们可以使用floyed来处理.然后就是套我上面的说的三种情况,具体可以代码中的注解

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<string>
    #include<cstring>
    #define in(i) (i=read())
    using namespace std;
    const int inf=0x3f3f3f;
    int read()
    {
        int ans=0,f=1;
        char i=getchar();
        while(i<'0'||i>'9'){if(i=='-') f=-1; i=getchar();}
        while(i>='0'&&i<='9') {ans=(ans<<1)+(ans<<3)+i-'0';i=getchar();}
        return ans*f;
    }
    int n;
    int dp[13][1<<13];
    int mp[13][13];
    void floyed()
    {
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
        return;
    }
    int main()
    {
        while(1) {
            int ans=inf; in(n);
            if(!n) break;
            n++;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    in(mp[i][j]);//输入每两个点之间的距离
            floyed();//求出n+1个点两两之间的最短距离
            memset(dp,inf,sizeof(dp));
            dp[1][1]=0;//默认以1为起点,集合内最开始状态为1<<(1-1)=1,所以dp[1][1]=0
            for(int i=1;i<(1<<n);i++)//枚举状态
                for(int j=1;j<=n;j++)//枚举每个点
                    if((i&(1<<(j-1)))!=0)//判断这个是否在集合中
                        for(int k=1;k<=n;k++)//如果不在就以它为中转点转移
                            if(!(i&(1<<(k-1))))
                                dp[k][i|(1<<(k-1))]=min(dp[k][i|(1<<(k-1))],dp[j][i]+mp[j][k]);//状态转移方程
            for(int i=2;i<=n;i++)
                ans=min(ans,dp[i][(1<<n)-1]+mp[i][1]);//还要回来才是一个环,因此还要加上到起点的距离
            cout<<ans<<endl;
        }
    }

    上述代码在洛谷应该是会T一个点的,因为重复使用位运算速度是会变慢的,所以我们可以提前处理出每个点左移多少位之后的数组,以及使用系统自带函数min也是很慢的

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<string>
    #include<cstring>
    #define MIN(a,b) (a)<(b)?(a):(b)
    #define in(i) (i=read())
    using namespace std;
    const int inf=0x3f3f3f;
    int read()
    {
        int ans=0,f=1;
        char i=getchar();
        while(i<'0'||i>'9') {if(i=='-') f=-1; i=getchar();}
        while(i>='0'&&i<='9') { ans=(ans<<1)+(ans<<3)+i-'0';i=getchar();}
        return ans*f;
    }
    int dp[1<<20][21],mp[21][21],st[21];
    int n;
    int main()
    {
        in(n);
        int ans=inf;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                in(mp[i][j]);
        memset(dp,inf,sizeof(dp));
        dp[1][1]=0; st[0]=1;
        for(int i=1;i<=20;i++) st[i]=st[i-1]<<1;//预处理
        for(int i=1;i<st[n];i++)
            for(int j=1;j<=n;j++)
                if( dp[i][j]!=dp[0][0] && i&st[j-1])
                    for(int k=1;k<=n;k++)
                        if(!(i&st[k-1]))
                            dp[i|st[k-1]][k]=MIN(dp[i|st[k-1]][k],dp[i][j]+mp[j][k]);
        for(int i=2;i<=n;i++)
            ans=MIN(ans,dp[st[n]-1][i]+mp[i][1]);
        printf("%d
    ",ans);
        return 0;
    }
    
    
    
    博主蒟蒻,随意转载.但必须附上原文链接
    http://www.cnblogs.com/real-l/
  • 相关阅读:
    gitlab搭建
    .NET Core 跨平台物联网开发:设置委托事件(二)
    .NET Core 跨平台物联网开发:连接阿里云IOT(一)
    Orange Pi 3 GPIO 笔记
    树莓派踩坑备忘录 -- 使用 Linux
    .NET Core / C# 开发 IOT 嵌入式设备的个人见解
    阿里云 IOT 对接设备开发 C# 开发设备对接阿里云 IOT平台
    跨平台开发 -- C# 使用 C/C++ 生成的动态链接库
    .NET Core 使用 EF 出错的解决方法
    arm 开发板更新 gcc/gcc++ | Debain 更新 gcc,无需编译直接更新 gcc
  • 原文地址:https://www.cnblogs.com/real-l/p/8589562.html
Copyright © 2020-2023  润新知