最短路径

来源:互联网 发布:提升淘宝搜索排名 编辑:程序博客网 时间:2024/06/08 00:12

【问题描述】
平面内给出 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;}
原创粉丝点击