BTSP——HDU2224The shortest path

(Bitonic Traveling Salesman Problem )
双调(欧几里得)旅行商问题

欧几里得旅行商问题是指在一个平面上有n个点,找出连接所有点的最短闭合路径的问题。这个问题虽然是一个求最短路的问题,但是其实和图论没什么很大关系,用的是动态规划的思想。

J.L. Bentley 建议通过只考虑双调(Bitonic)路径来求得最短路径。双调路径是指从一个最左端的点出发,严格向右端行进(x左 < x右)直至到达最右端,然后再从最右端开始向最左端行进直至起点。这样形成的一个路径为双调路径。
如图a,b都是最短路径,但是a为非双调最短闭合路径,b为双调最短闭合路径。
将问题转化为求双调最短路径后,该问题的算法复杂度就变成了O(n^2)。

在利用双调TSP来解决问题时,我们暂且设没有任何两点x坐标相同。

1.将平面上所有点按横坐标x升序排序(O(nlogn))
2.子结构:我们定义d[i][j] (i>=j)为i经过起点到达j点的路径长度,i到起点x单调变小,起点到j单调变大,d[n][n]即为答案。定义dist[i][j] 为i<->j之间的直接距离。
3.DP时会有三种情况
当j < i-1时 d[i][j] = d[i][i-1] + dist[i][i-1];
当j = i-1时 d[i][j] = min(d[i][j],d[j][k] + dist[i][k]) (1 < = k < j);
当j = i 时 d[i][j] = min(d[i][j],d[j][k] + dist[i][k]) (1< = k < j);
第二和第三种情况可以合并一下。

HDU2224The shortest path

题意如上,一道典型BTSP的模板题。题目入口

AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
typedef pair <int,int> P;
const int INF = 0x3f3f3f3f;
const int maxn = 200+10;
int n;

struct Point
{
    int x,y;
    Point (int xi = 0,int yi = 0) : x(xi),y(yi) {}
    bool operator < (const Point &a)const
    {
        return this->x < a.x;
    }
};


double Distance(Point a, Point b)
{
    int X = a.x - b.x;
    int Y = a.y - b.y;
    return sqrt(X*X+Y*Y);
}

Point p[maxn];

void input()
{
    for(int i = 0; i < n; i++)
        scanf("%d%d",&p[i].x, &p[i].y);
}

double dist[maxn][maxn];

void Build()
{
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
              dist[i][j] = dist[j][i] = Distance(p[i],p[j]);
}

double d[maxn][maxn];

void solve()
{
    sort(p,p+n);//排序
    //cout << p[0].x << endl;
    Build();//建图
    //初始化数组
    for(int i = 0; i < n; i++)
        for(int j = 0; j <n; j++)
                     d[i][j] = (double)INF;
    d[0][0] = 0;
    d[0][1] = d[1][0] = dist[0][1];
    for(int i = 0; i < n; i++)
        for(int j = 0; j <= i; j++)
    {
        if(j < i-1)
            d[i][j] = d[i-1][j] + dist[i][i-1];
        else
        {
            for(int k = 0; k < j; k++)
                d[i][j] = min(d[i][j],d[j][k] + dist[i][k]);
        }
        //cout << i << j << " " << d[i][j] << endl;
    }
   // for(int i = 0; i < n-1; i++)
       // d[n-1][n-1] = min(d[n-1][n-1],d[n-1][i] + dist[n-1][i]);
}

int main()
{
    while(~scanf("%d",&n))
    {
        input();
        solve();
        printf("%.2f\n",d[n-1][n-1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值