jzoj3510-最短路径【dp】

正题


大意

有n个点,开始只能从前面的点走到后面的点,到达点n后返回,之后就只能从后面的点走到前面的点,从点1出发,要求每个点(除了点1)都必须且只能经过一遍,并回到点1的最短路径。给出每个点的坐标。
有两个点,一个必须在点n之前到达,一个必须在点n之后到达。


解题思路

因为这个可以看成一个无向图,所有我们可以将路径分为去和回两条,然后把回去的路径反过来,这就变成了求两条路径覆盖所有点并且不重复还有两个特殊点。
我们考虑dp,由于数据范围可以 n 2 ,我们可以用 f [ i ] [ j ] 表示第一条路径到达第i个点,第二条路径到达第j个点时的最短路。然后我们每次让i和j交替向前,用一个k表示第i和第j个点先前走的最近距离。
然后动态转移:

f [ i ] [ k ] = m i n { f [ i ] [ j ] + d i s [ j ] [ k ] }

f [ k ] [ j ] = m i n { f [ i ] [ j ] + d i s [ i ] [ k ] }

两个特殊点就保证一个在第一维中没有值一个保证在第二维中没有值就好了。


代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 1010
#define inf 2147483647/3
using namespace std;
double x[N],y[N],dis[N][N],f[N][N];
int n,b1,b2;
double Dis(double x1,double y1,double x2,double y2)
{return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}
int main()
{
    scanf("%d%d%d",&n,&b1,&b2);
    b1++;b2++;
    for (int i=1;i<=n;i++)
      scanf("%lf%lf",&x[i],&y[i]);
    for (int i=1;i<n;i++) 
      for (int j=i+1;j<=n;j++)
        dis[i][j]=dis[j][i]=Dis(x[i],y[i],x[j],y[j]);
        //计算距离
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++) f[i][j]=inf;//初始化
    f[1][1]=0;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=n;j++)
      {
        if (i==j&&i!=1)continue;
        int k=max(i,j)+1;
        if (k==n+1)//特判
        {
          if (i!=n) f[n][n]=min(f[n][n],f[i][j]+dis[i][n]);
          if (j!=n) f[n][n]=min(f[n][n],f[i][j]+dis[j][n]);
          continue;
        }
        if (k!=b1) f[i][k]=min(f[i][k],f[i][j]+dis[j][k]);
        if (k!=b2) f[k][j]=min(f[k][j],f[i][j]+dis[i][k]);//动态转移
      }
    printf("%0.2lf",f[n][n]);
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/81105198
今日推荐