【动态规划】bitonic tour双调旅行商问题

Description

欧几里德旅行商(Euclidean Traveling Salesman)问题也就是货郎担问题一直是困扰全世界数学家、计算机学家的著名问题。现有的算法都没有办法在确定型机器上在多项式时间内求出最优解,但是有办法在多项式时间内求出一个较优解。

为了简化问题,而且保证能在多项式时间内求出最优解,J.L.Bentley提出了一种叫做bitonic tour(双调旅程)的哈密尔顿环游来解决问题。

它的要求是任意两点(a,b)之间的相互到达的代价dist(a,b)=dist(b,a)且任意两点之间可以相互到达,并且环游的路线只能是从最西端单向到最东端,再从最东端返回最西端,并且是一个哈密尔顿回路。图b显示了同样的7个点的问题的最短双调路线,在这种情况下,多项式的时间的算法是有可能的。

著名的NPC难题的简化版本

现在笛卡尔平面上有n(n<=1000)个点,每个点的坐标为(x,y)(-2^31< x,y< 2^31,且为整数),任意两点之间相互到达的代价为这两点的欧几里德距离,现要你编程求出最短bitonic tour。

Input

包含若干组数据,每组数据格式如下:

第一行一个整数n

接下来n行,每行两个整数x,y,表示某个点的坐标。

输入中保证没有重复的两点, 保证最西端和最东端都只有一个点。

Output

对于每组数据,输出一行,即最短回路的长度,保留2位小数。

Input sample

7
0 6
1 0
2 3
5 4
6 1
7 5
8 2
3
1 1
2 3
3 1
4
1 1
2 3
3 1
4 2

Output sample

25.58
6.47
7.89

————————————————分割の线————————————————

分析

首先是预处理,d[i][j]表示从i到j的距离。 因为是从1点出发到达n点再回到1点,所以可以看作是两个旅行商均从1点出发到达n点,期间两人不经过同一点。
如此可以定义f[i][j]表示一个旅行商到达i点,另一个旅行商到达j点,从1到max(i,j)点都已经走过后的最小值。因为f[i][j]==f[j][i],所以可以规定:

i < j,f[i][j]表示一个旅行商到达i点,另一个旅行商到达j点,且从1到j均已走过的最小值。

因为第j+1个点,会有两条线路中的其中一条经过,所以可以得出状态更新方程(本题使用刷表法):

f[i][j+1]=max(f[i][j+1],f[i][j]+d[j][j+1]);
f[j][j+1]=max(f[j][j+1],f[i][j]+d[i][j+1]);

求最短路的答案

ans=min(ans,f[i][n]+d[i][n]);

完整代码如下:

#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1111;
struct data
{
    double x,y;
}g[maxn];//储存每个点的横纵坐标
double d[maxn][maxn],f[maxn][maxn];
int i,j,k,n;
bool cmp(data a,data b)
{
    return a.x<b.x;
}//按照横坐标排序,貌似有些Oj的横坐标是有序的
double mdis(data a,data b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}//计算长度
int main()
{
    while(scanf("%d",&n)!=EOF)//判断是否文件结束
    {
    for(i=1;i<=n;i++)
        scanf("%lf%lf",&g[i].x,&g[i].y);
    sort(g+1,g+n+1,cmp);//读入坐标并排序
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
        {
            d[i][j]=mdis(g[i],g[j]);//计算长度
            f[i][j]=1e30;//给f赋初值
        }
    f[1][2]=d[1][2];
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
        {
            f[i][j+1]=min(f[i][j+1],f[i][j]+d[j][j+1]);
            f[j][j+1]=min(f[j][j+1],f[i][j]+d[i][j+1]);
        }//更新i,j之后的状态
    double ans=1e30;
    for(i=1;i<n;i++)
        ans=min(ans,f[i][n]+d[i][n]);//扫描最小值
    printf("%.2lf\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/80001023
今日推荐