解题思路
比赛的时候打了一个20分暴力。。。(我就是菜狗)
正解:DP
首先可以把题目转换一下,把从头到尾走一遍再从尾到头走一遍转换成从头到尾走两遍,所以,就是要求从头到尾的两条不相交路径的最小值。
设lyx[i][j]表示一条路径走到i,一条路径走到j时的最小值,明显,i和j不能相等。
然后设k为下一个走到的点,
k
=
m
a
x
(
i
,
j
)
+
1
k=max(i,j)+1
k=max(i,j)+1(这样是可以保证经过了每一个点的。
转移方程:
l
y
x
[
i
]
[
k
]
=
m
i
n
(
l
y
x
[
i
]
[
k
]
,
l
y
x
[
i
]
[
j
]
+
f
[
j
]
[
k
]
)
;
lyx[i][k]=min(lyx[i][k],lyx[i][j]+f[j][k]);
lyx[i][k]=min(lyx[i][k],lyx[i][j]+f[j][k]);(特判k!=b1)
l
y
x
[
k
]
[
j
]
=
m
i
n
(
l
y
x
[
k
]
[
j
]
,
l
y
x
[
i
]
[
j
]
+
f
[
i
]
[
k
]
)
;
lyx[k][j]=min(lyx[k][j],lyx[i][j]+f[i][k]);
lyx[k][j]=min(lyx[k][j],lyx[i][j]+f[i][k]);(特判k!=b2)
PS:注意理解“有两个特殊点 b1 和 b2“,意思是b1必须在从头到尾的那条路径上,b2必须在从尾到头的那条路径上。
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n,b1,b2,k,num,u[1010],v[1010];
double ans,m,f[1010][1010],lyx[1010][1010];
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d%d%d",&n,&b1,&b2);
b1+=1,b2+=1;
for(int i=1;i<=n;i++)
scanf("%d%d",&u[i],&v[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
f[i][j]=f[j][i]=sqrt(pow(double(u[i]-u[j]),2)+pow(double(v[i]-v[j]),2));
memset(lyx,0x7f,sizeof(lyx));
lyx[1][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=j||(i==j&&i==1))
{
int k=max(i,j)+1;
if(k==n+1)
{
if(i!=n)lyx[n][n]=min(lyx[n][n],lyx[i][n]+f[i][n]);
if(j!=n)lyx[n][n]=min(lyx[n][n],lyx[n][j]+f[j][n]);
continue;
}
if(k!=b1)lyx[i][k]=min(lyx[i][k],lyx[i][j]+f[j][k]);
if(k!=b2)lyx[k][j]=min(lyx[k][j],lyx[i][j]+f[i][k]);
}
}
}
printf("%.2f",lyx[n][n]);
}