最短路径

【问题描述】
平面内给出 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;
}
原文地址:https://www.cnblogs.com/wutongtong3117/p/7673538.html