• 旅游 双调欧几里得旅行商算法


    PDF链接:点我

    vj题目链接:点我

    题目:

    John Doe, a skilled pilot, enjoys traveling. While on vacation, he rents a small plane and starts visiting beautiful places. To save money, John must determine the shortest closed tour that connects his destinations. Each destination is represented by a point in the plane pi =< xi , yi >. John uses the following strategy: he starts from the leftmost point, then he goes strictly left to right to the rightmost point, and then he goes strictly right back to the starting point. It is known that the points have distinct x-coordinates. Write a program that, given a set of n points in the plane, computes the shortest closed tour that connects the points according to John’s strategy.

    Input The program input is from a text file. Each data set in the file stands for a particular set of points. For each set of points the data set contains the number of points, and the point coordinates in ascending order of the x coordinate. White spaces can occur freely in input.

    The input data are correct.

    Output

    For each set of data, your program should print the result to the standard output from the beginning of a line. The tour length, a floating-point number with two fractional digits, represents the result. Note: An input/output sample is in the table below. Here there are two data sets. The first one contains 3 points specified by their x and y coordinates. The second point, for example, has the x coordinate 2, and the y coordinate 3. The result for each data set is the tour length, (6.47 for the first data set in the given example).

    Sample Input

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

    Sample Output

    6.47 7.89

    题意:

    给你n个点,你需要把它们连成一个环,且每一个点只能经过一次,问你这个环的长度最小是多少。(输入数据保证不存在两个点的x值相同

    题解:

    我们我们首先看一下什么是旅行商(Bitonic)问题:

    平面上n个点,确定一条连接各点的最短闭合旅程。这个解的一般形式为NP的(在多项式时间内可以求出)

    算法复杂度为O(n^2)

    我们首先定义一下变量:

    路径Pi,j(i<=j),对于路径Pi,j来说,它包含的点为p1,p2,..pj。其中pi点为起始点,pi,j表示从pi点开始走,一直向左走到p1然后从p1点沿不同的路径向右走直到pj

    数组d[i,j]表示pi,j路径上的最小Bitonic路线。很容易看出,题目所求的即是d[n,n]。

    dist(i,j)表示i点和j点的距离(两点之间距离

    求解过程:

    1、对所有点按照x横坐标的值从小到大排序(所以这个算法的前提就是不能存在x值相同的两个点

    2、首先d[1,1]无意义,d[1,2]就是p1,p2两点的距离dist(1,2)

    3、对于路径Pi,j来说,如果pi不是pj的相邻点(相邻:i=j-1),d[i,j]=d[i,j-1]+dist(j-1,j)

    由定义可知,点Pj-1一定在路径Pj-Pi上,而且又由于i<j-1,因此Pj的左边的相邻点一定是Pi-1.因此可以得出上述等式。

    4、当i=j-1时,对于pj点来说,有一侧必与pi点相连,此时要判断pj点的另一侧与哪个点相连。根据最优解的结构,此时,定义整数k(1<=k<j-1),计算每个d[k,j-1]+dist(k,j)的值,选择其中最小的一个作为d[i,j]的值,并且记录k值,将pj与pk相连。

    d(i,j) = min{d(k,i) + dist(j,k)},

    5、最后

    题目的最优解为d[n,n],此时,分析当pn点未连通的情况。因为点是排好序的,而且根据题意中的严格方向的要求,可以知道pn点必有一侧是跟pn-1点相连的,那么此时d[n-1,n]应该为pn-1,n的最优解。采用反证法,如果存在d’[n-1,n]<d[n-1,n],那么用d’[n-1,n]来替换d[n-1,n]就可以得到一个更小的d[n,n],与已知d[n,n]为最优解矛盾,因此假设不成立。

    所以,最后答案:dp[n][n]=dp[n-1][n]+dist(n-1,n)

    本题就是例题:

     代码:

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <map>
    #include <queue>
    #include <set>
    #include <ctime>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int maxn = 1000 + 10;
    const long long ll_INF=0x3f3f3f3f3f3f3f3fLL;
    const int inf=200000000;
    const int INF=0x3f3f3f3f;
    double dp[maxn][maxn];
    struct Point
    {
        double x,y;
    }point[maxn];
    bool cmp(Point x,Point y)
    {
        return x.x<y.x;
    }
    double dist(int i,int j)
    {
        return sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y));
    }
    int main()
    {
        int n;
        while(~scanf("%d",&n))
        {
            for(int i=1;i<=n;++i)
            {
                scanf("%lf%lf",&point[i].x,&point[i].y);
            }
            sort(point+1,point+n+1,cmp);
            dp[1][2]=dist(1,2);
            for(int i=3;i<=n;++i)
            {
                for(int j=1;j<=i-2;++j)
                {
                    dp[j][i]=dp[j][i-1]+dist(i,i-1);
                }
                dp[i-1][i]=INF;
                for(int k=1;k<=i-2;++k)
                {
                    double temp=dist(k,i)+dp[k][i-1];
                    if(temp<dp[i-1][i])
                    {
                        dp[i-1][i]=temp;
                    }
                }
            }
            dp[n][n]=dp[n-1][n]+dist(n-1,n);
            printf("%.2lf
    ",dp[n][n]);
        }
        return 0;
    }
  • 相关阅读:
    C/C++分别读取文件的一行
    (转载)C库函数strtok()
    (转载)C++常量折叠和C语言中const常量对比
    ssh
    ubuntu ufw
    uplevel
    ubuntu lucid source.list
    tail
    socket client with proc
    pack forget
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/13899210.html
Copyright © 2020-2023  润新知