天天看点

最短路径(paths)题解,,,水博客

题目描述:

平面内给出 n 个点,记横坐标最小的点为 A,最大的点为 B,现在小 Y 想要知道在
每个点经过一次(A 点两次)的情况下从 A 走到 B,再回到 A 的最短路径。但他是个强
迫症患者,他有许多奇奇怪怪的要求与限制条件:
1.从 A 走到 B 时,只能由横坐标小的点走到大的点。
2.由 B 回到 A 时,只能由横坐标大的点走到小的点。
3.有两个特殊点 b1 和 b2, b1 在 0 到 n-1 的路上,b2 在 n-1 到 0 的路上。
请你帮他解决这个问题助他治疗吧!
           

输入格式:

第一行三个整数 n,b1,b2,( 0 < b1,b2 < n-1 且 b1 <> b2)。n 表示点数,从 0 到 n-1 编号,b1 和 b2 为两个特殊点的编号。
    以下 n 行,每行两个整数 x、y 表示该点的坐标(0 <= x,y <= 2000),从 0 号点顺序给出。Doctor Gao 为了方便他的治疗,已经将给出的点按 x 增序排好了。
           

输出格式:

输出仅一行,即最短路径长度(精确到小数点后面 2 位)
           

样例输入:

5 1 3
1 3
3 4
4 1
7 5
8 3
           

样例输出:

18.18
           

简析:

题目大意是:给定n个点及其坐标,要求从起点和终点相向而行,使得总距离最短,并要求包含各自的指定点,且除1,n两点外,不能包含相同点。
  首先,为了方便,我们将 从n向1出发的路线视为从1向n的路线,并将点的编号加一(我实在不喜欢0到n-1)。
  然后,本人用dp做的。数组f[i][j]表示线路一到达i点,线路二到达j点时的总路径长度。
  为了保证不出现到达同一个点的情况,同时保证每一个点都用上,我们事先找出一个目标点,再用现在的状态去更新,来判断这个点应该加入哪一条线路,规定目标点的计算方式为 max(i,j)+1。
  因此,转移方程为:(dis[i][j]表示i,j间的距离
  int k=max(i,j)+1;
  f[1][1]=0;
  f[i][k]= min(f[i][k],f[i][j]+dis[j][k])
  f[k][j]= min(f[k][j],f[i][j]+dis[i][k])
  当然,要考虑一些特殊情况,例如b1,b2点,k=max(i,j)+1时,(i==n||j==n)。
           

代码 :

#include<stdio.h>
#include<string.h>
#include<math.h>
#define max(a,b) a>=b?a:b
#define min(a,b) a>=b?b:a
int scan(){             //读入优化
    int x=;
    char c=getchar();
    while(c>'9'||c<'0')
        c=getchar();
    while(c>='0'&&c<='9')
        x=(x<<)+(x<<)+c-'0',
        c=getchar();
    return x;
}
struct node{
    int x,y;
};
int n,b1,b2;
node poi[];
double dis(int i,int j){
    int x=(poi[i].x-poi[j].x)*(poi[i].x-poi[j].x);
    int y=(poi[i].y-poi[j].y)*(poi[i].y-poi[j].y);
    return sqrt(x*+y*);
}
double d[][],f[][];
int main(){
    n=scan();
    b1=scan()+;b2=scan()+;
    for(int i=;i<=n;i++){
        poi[i].x=scan();
        poi[i].y=scan();
    }
    for(int i=;i<=n;i++)
        for(int j=;j<=n;j++){
            d[i][j]=d[j][i]=dis(i,j);
            f[i][j]=;
        }
    f[][]=;  //都在起点时距离为0
    //已经将从n到1视为从1到n
    for(int i=;i<=n;i++)
        for(int j=;j<=n;j++){
            if(i!=j||i==){
                int k= max(i,j) ;
                k++;
                if(k==n+){ //线路一或线路二到达终点
                    if(i!=n)
                        f[n][n]= min(f[n][n],f[i][j]+d[i][n]);
                    if(j!=n)
                        f[n][n]= min(f[n][n],f[i][j]+d[j][n]);
                    continue;
                }
                if(k!=b1)  //b1只能在线路一,因此,将k加入线路二时,要看一下k是不是b1
                    f[i][k]= min(f[i][k],f[i][j]+d[j][k]);
                if(k!=b2)  //同理
                    f[k][j]= min(f[k][j],f[i][j]+d[i][k]); 
            }
        }
    printf("%.2lf",f[n][n]);  //保留两位小数
}