J.L. Bentley 建议通过只考虑双调旅程(bitonic tour)来简化问题,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。下图(b)显示了同样的7个点的最短双调路线。在这种情况下,多项式的算法是可能的。事实上,存在确定的最优双调路线的O(n*n)时间的算法。

 
     注:在一个单位栅格上显示的平面上的七个点。 a)最短闭合路线,长度大约是24.89。这个路线不是双调的。b)相同点的集合上的最短双调闭合路线。长度大约是25.58。
这是一个算导上的思考题15-1。
首先将给出的点排序,关键字x,重新编号,从左至右1,2,3,…,n。
定义d[i][j],表示结点i到结点j之间的距离。
定义dp[i][j],表示从i连到1,再从1连到j,(注意,i>j,且并没有相连。)
.png)
对于任意一个点i来说,有两种连接方法,一种是如图(a)所示,i与i-1相连,另一种呢是如图(b),i与i-1不相连。
根据双调旅程,我们知道结点n一定与n相连,那么,如果我们求的dp[n][n-1],只需将其加上d[n-1][n]就是最短双调闭合路线。
根据上图,很容易写出方程式:
dp[i][j]=dp[i-1][j]+d[i][i-1];
dp[i][i-1]=min(dp[i][i-1],dp[i-1][j]+d[j][i]);
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 1010;
int dp[maxn][maxn];
int d[maxn][maxn];
struct point
{
    int x, y;
}a[maxn];
int dis(int i, int j)                   //计算两点之间的距离
{
     int p,q;
     if(a[i].y>a[j].y)
          q=(360+a[j].y-a[i].y)%360;
     else
          q=(360+a[i].y-a[j].y)%360;
          p=abs(a[i].y-a[j].y)>q?q:abs(a[i].y-a[j].y);                     //求出小弧的长度
    return (abs(a[i].x-a[j].x)*400+p);                  //距离=两点的轨道差*400+两点的扇区差
}
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t-- )
    {
         scanf("%d", &n);
         a[1].x=0;
         a[1].y=0;                                      //把起点(0,0)加上
        for(int i = 2; i <= n+1; i++)
            scanf("%d %d", &a[i].x, &a[i].y);
        for(int i = 1; i <= n+1; i++)
        {
            for(int j = 1; j <= n+1; j++)
            {
                d[i][j] = dis(i, j);               //d[i][j]为i点到j点的距离
            }
        }
        dp[1][2] = d[1][2];                 
        for(int i = 3; i <= n+1; i++)
        {
            for(int j = 1; j < i-1; j++)
            {
                 dp[j][i] = dp[j][i-1] + d[i-1][i];                /*      dp[j][i]为j点到1点,再从1点到i点的距离,这一步是为下一循环求dp[i][i+1]做准备,其实就是图a      */
            }
            dp[i-1][i] = 999999999;
            for(int j = 1; j < i-1; j++)
            {
                int sum = dp[j][i-1] + d[j][i];
                if(dp[i-1][i] > sum)
                    dp[i-1][i] = sum;                         /*     dp[i-1][i]为i-1点到1点,再从1点到i点的最短距离,这个距离只要加上边d[i-1][i]就是从1点到i点的最短闭合旅程,其实就是图b      */      
            }
        }
        dp[n+1][n+1] = dp[n][n+1] + d[n][n+1];
        printf("%d\n", dp[n+1][n+1]+10*n);        /*   dp[n+1][n+1]就是最终的最短闭合旅程,n+1点到1点,再从1点到n+1点的最短距离 ,10*n为读取点中数据的时间 */
    }
    return 0;
}leetCode解题报告5道题(十),布布扣,bubuko.com
原文地址:http://blog.csdn.net/ljphhj/article/details/26134395