最短路径(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=0;
    char c=getchar();
    while(c>'9'||c<'0')
        c=getchar();
    while(c>='0'&&c<='9')
        x=(x<<1)+(x<<3)+c-'0',
        c=getchar();
    return x;
}
struct node{
    int x,y;
};
int n,b1,b2;
node poi[1010];
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*1.0+y*1.0);
}
double d[1010][1010],f[1010][1010];
int main(){
    n=scan();
    b1=scan()+1;b2=scan()+1;
    for(int i=1;i<=n;i++){
        poi[i].x=scan();
        poi[i].y=scan();
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            d[i][j]=d[j][i]=dis(i,j);
            f[i][j]=20000010.0;
        }
    f[1][1]=0.0;  //都在起点时距离为0
    //已经将从n到1视为从1到n
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(i!=j||i==1){
                int k= max(i,j) ;
                k++;
                if(k==n+1){ //线路一或线路二到达终点
                    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]);  //保留两位小数
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值