• 旅行商问题(TSP)之动态规划解法


    http://soj.sysu.edu.cn/show_problem.php?pid=1000&cid=1769

    sicily Traveling Salesman Problem

    有编号1到N的N个城市,问从1号城市出发,遍历完所有的城市并最后停留在N号城市的最短路径长度。

    Input

    第一行整数 T :T组数据 (T<=20)

    每个case 读入一个N( 2 <= N <= 20),接着输入N行,第i行有两个整数 xi,yi表示
    第i个城市坐标轴上的坐标,两个城市的距离定义为欧氏距离。
    Output

     每个case输出一个浮点数表示最短路径。四舍五入保留两位小数。

    Sample Input
    1
    4
    0 0
    1 0
    0 1
    1 1
    Sample Output
    3.41

      这题真是难,而且网上也没找到比较深入的分析......旅行商问题还没找到多项式解,如果所有城市都遍历一遍,是n!的复杂度,显然很大;还有,用贪心来解这种最短路问题是不一定能得到正确解的,因为需要每个城市都走一遍,跟dijkstra的那种最短路是不一样的。因为这题n<=20,我们可以用动态规划来做...
      进入正题,首先定义状态:dp[S][j],S为集合,代表经过哪些城市,j为终点站,该状态表示经过S这些的城市到达j需要的最短路径,至于这个状态是怎么想到的,我也不知道...老师说的...我能理解已经很不错了...
    ---------------------------------------------------------------------------------------------
      然后,先来点预备知识,怎么表示集合呢?我们用一个二进制数来表示集合,这里有四个城市(下标从0开始吧)0,1,2,3,四个城市,我们用3位二进制表示,000~111,假设现在S=5,就相当于101,就相当于我们经过了城市1,3,为什么?因为看1<<(i-1) & S是1还是0。
    假如i是城市1那么1<<(1-1) & S = 1;
    假如i是城市2,
    1<<(2-1) & S = 0;
    假如i是城市3,1<<(3-1) & S = 1;
    是不是刚好符合呢!通过这个判断就知道当前经过哪些城市了。
    ----------------------------------------------------------------------------------------------
    ok,可以开始动规了!
    那么,接下来又像之前那样先举些栗子:

    先建图(就用样例的图吧),,图自己画一下吧,这样才容易理解,注意是双向的哦!然后,因为起点是0,所以我们不把0考虑进去咯!
    还有,下面的集合S要看成是集合来理解,只是算的时候用二进制数算,理解的时候要用集合!
    看状态dp[1,2][2],表示经过1,2城市到达2的最短路径,那跟什么有关呢?是不是就是经过城市1到达城市1的最短路径,加上1到2的距离?
    就是dp[1][1]+dis[1][2],那么这个经过城市1的到达城市1(有点拗口)的最短路径是不是就是,从城市0出发到城市1的距离呢,dis[0][1],很明显,dp数组的初始值,就是dp[0][i]=dis[0][i]了,所以这里我们可以初始化dp[1][i]了,而且这个转移方程也有一点苗头了:
    dp[S][j] = min(dp[S-{j}][i]+dis[i][j], dp[S][j]),这里的i指的是在那些经过的城市里面选的。

    好像栗子不太够,再来一个:
    dp[1,2,3][3],取决于前一个经过城市2又或者是1->dp[1,2][2]+dis[2][3] or dp[1,2][1]+dis[1][3],再继续,看前一种:
    经过1,2到达2,好理解,就是经过1到达1再加上1,2的距离,跟上一个栗子一样;后面一种,经过1,2到达1,就是经过2到达1再加1,2的距离,而经过2到达1,就是0,2的距离再加上2,1的距离,这种还说不定会快一下,谁知道呢,所以就要不断更新!
    还是感觉下标好乱......

    最后看一下下标从1开始的代码:
     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 
     6 using namespace std;
     7 
     8 double const INF = 1e8;
     9 
    10 struct point {
    11     int x;
    12     int y;
    13 }p[25];
    14 
    15 double d(point a, point b)
    16 {
    17     return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
    18 }
    19 
    20 double dis[25][25], dp[1111110][25];
    21 
    22 int main()
    23 {
    24     int t;
    25     scanf("%d", &t);
    26     while(t--)
    27     {
    28         int n;
    29         scanf("%d", &n);
    30         int count=1;
    31         double ans = INF;
    32         
    33         for(int i=0; i<n-1; i++) //集合 10000
    34             count <<= 1;
    35         
    36         for(int i=1; i<=n; i++)
    37             scanf("%d%d", &p[i].x, &p[i].y);
    38         
    39         for(int i=1; i<count; i++) //注意初始化的下标 
    40             for(int j=1; j<=n; j++)
    41                 dp[i][j] = INF;
    42         
    43         for(int i=1; i<=n; i++)
    44             for(int j=1; j<=n; j++)
    45                 dis[i][j] = d(p[i], p[j]);
    46         
    47         for(int i=1; i<=n; i++)
    48             dp[1][i] = dis[1][i];
    49             
    50         for(int s=2; s<count; s++) //集合 
    51             for(int j=2; j<=n; j++)
    52             {
    53                 for(int i=2; i<=n; i++)
    54                 {
    55                     int temp = 1 << (i-1);
    56                     if(temp & s)
    57                         dp[s][j] = min(dp[s-temp][i]+dis[i][j], dp[s][j]);
    58                 }
    59             }
    60         ans = dp[count-1][n];
    61         printf("%.2lf
    ", ans);
    62     }
    63     return 0;
    64 }
    
    
    
     
    虽然感觉理解深了一点,但是代码的确不好写啊......

  • 相关阅读:
    python的类的继承和多继承
    python的类访问控制、私有属性
    python的面向对象、类方法和静态方法
    python的序列化和反序列化
    python高阶函数、柯里化、装饰器、参数注解
    python函数参数、参数解构、作用域、递归及匿名函数
    python内置函数列表解析、生成器
    python内置函数字典(dict)
    python内置函数集合(set)
    定制自己的打印函数---进阶篇
  • 原文地址:https://www.cnblogs.com/dominjune/p/4401425.html
Copyright © 2020-2023  润新知