zoukankan      html  css  js  c++  java
  • 最短路径

    【问题描述】
    平面内给出 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 位)

    【样例输入输出】
    paths.in
    5 1 3
    1 3
    3 4
    4 1
    7 5
    8 3

    paths.out
    18.18

    【样例解释】
    最短路径:0->1->4->3->2->0

    【数据范围】
    20%的数据 n<=20
    60%的数据 n<=300
    100%的数据 n<=1000
    对于所有数据 x,y,b1,b2如题目描述

    分析:
    考试的时候秒想dp
    f[i][j]表示来点走到i,去点走到j时的最小距离
    然而状态转移方程想不好,JJ。。。

    发现自己这方面的问题做的不大好,正好查缺补漏,
    所以这篇文就来解决:
    图上两条不相交路径最短问题

    用f[i][j]表示第一个点走到i,第二个点(回去的那个点)走到j的最优值。
    为了保证更新时不会更新出f[i][i],而且每个点都会在路径上,
    我们每次用f[i][j]去更新点max(i,j)+1,所以转移方程为:

    f[1][1]=0;
    k=max(i,j)+1,
    f[k][j]=min(f[k][j],f[i][j]+dis(i,k));
    f[i][k]=min(f[i][k],f[i][j]+dis(j,k));

    dis(i,j)为从i直接走到j点的距离.

    那就要问了,这样真的可以保证所有的点都到达过吗。。。
    因为i,j使从1到n循环的,所以每个点都可以分配到来路或去路

    对于两个特殊点和max(i,j)==n的情况特判处理
    如下:

    if (j==n) f[n][n]=min(f[n][n],f[i][n]+dis(i,n));
    if (i==n) f[n][n]=min(f[n][n],f[n][j]+dis(j,n));
    //一个节点到头了,只能动另一个节点

    if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+dis(j,k)); //回的时候不走b1
    if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+dis(i,k)); //去的时候不走b2

    唯一需要注意的
    这里写图片描述
    i!=j 很好理解
    i==1,其实就是两个点都可在起点,这样的状态是合法的,
    能够进行下面转移

    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    
    using namespace std;
    
    const double eps=1e-8;
    const int N=1001;
    int n,b1,b2;
    double po[N][2];
    double f[N][N];
    
    
    double dis(int bh1,int bh2)
    {
        double x=(double)(po[bh1][0]-po[bh2][0])*(po[bh1][0]-po[bh2][0]);
        double y=(double)(po[bh1][1]-po[bh2][1])*(po[bh1][1]-po[bh2][1]);   
        return (double)sqrt(x+y);
    }
    
    void doit()
    {
        int i,j;
        for (i=1;i<=n;i++)
            for (j=1;j<=n;j++) f[i][j]=1000000;  //初始化,只能用循环操作 
        f[1][1]=0;
        for (i=1;i<=n;i++)
            for (j=1;j<=n;j++)
            if (i!=j||i==1)
            {
                int k=max(i,j)+1;
                if (k==n+1)   //特判 
                {
                    if (j==n) f[n][n]=min(f[n][n],f[i][n]+dis(i,n));
                    if (i==n) f[n][n]=min(f[n][n],f[n][j]+dis(j,n));
                }
                else
                {
                    if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+dis(j,k));  //回的时候不走b1 
                    if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+dis(i,k));  //来的时候不走b2 
                }
            } 
        printf("%0.2lf",f[n][n]);
    }
    
    int main()   
    {
        freopen("paths.in","r",stdin);  
        freopen("paths.out","w",stdout);
        scanf("%d%d%d",&n,&b1,&b2);
        b1++;b2++;
        for (int i=1;i<=n;i++)
            scanf("%lf%lf",&po[i][0],&po[i][1]);
        doit();
        return 0;
    }
  • 相关阅读:
    P5136 sequence(矩阵快速幂)
    P5135 painting(组合数)
    CF888E Maximum Subsequence(meet in the middle)
    P4463 [国家集训队] calc(拉格朗日插值)
    CF364D Ghd(随机化)
    P3270 [JLOI2016]成绩比较(拉格朗日插值)
    bzoj3453: tyvj 1858 XLkxc(拉格朗日插值)
    P4593 [TJOI2018]教科书般的亵渎(拉格朗日插值)
    tomcat8版本实现虚拟主机
    NFS网络文件系统方案
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673537.html
Copyright © 2011-2022 走看看