题目链接:https://www.nowcoder.com/acm/contest/201/L
题目大意:给你n个圆和两条直线,在圆上,和圆内和直线上行走不消耗体力。
在其他位置上由S点走到T点消耗的体力为S和T的欧几里得距离。Hifumi Takimoto想从 L1 出发,走到 L2 。请计算最少需要多少体力。
思路:因为圆与直线的位置可以任意。从L1到L2,我们计算从c大的直线的到c小的直线距离就好了,如图L1->L2。
其实思路就是dp最短路。我们先按照圆与L1的距离排序。(排序依据不只是距离,因为可能在直线外)
int px(O pa, O pb)
{
if(a*pa.x+b*pa.y+c1<0&&a*pb.x+b*pb.y+c1<0)//都在L1左边,按照距离L1的距离从大到小
return pa.s<pb.s;
else if(a*pa.x+b*pa.y+c1>0&&a*pb.x+b*pb.y+c1>0)//都在L1右边,按照距离L1的距离从小到大
return pa.s>pb.s;
else
return a*pa.x+b*pa.y+c1<a*pb.x+b*pb.y+c1?0:1;//一左一右,左在右前
}
所以递推公式(dp[i].s为初始到L1的距离)
dp[i].s=min(dp[j].s,d[i].s), j<i。
因为n<=1000;直接两种循环暴力。
先初始化s[i].s=d。
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
//d(ij)如图的黄色虚线距离。
dp[j].s=(dp[i].s, d(ij));
}
}
这样求出所有圆到L1的距离,在加上该圆的到L2的距离。
再求dp[i].s的最小距离就行了。
思考:这题当时写了两个小时,因为犯了不少错误。最后AC的瞬间,高兴跳起来的欢呼。这可能就是ACM的魅力吧!。
#include<bits/stdc++.h>
using namespace std;
struct O
{
double x;
double y;
double r;
double s;
};
O w[1005];
long long n, a, b, c1, c2;
int px(O pa, O pb)
{
if(a*pa.x+b*pa.y+c1<0&&a*pb.x+b*pb.y+c1<0)
return pa.s<pb.s;
else if(a*pa.x+b*pa.y+c1>0&&a*pb.x+b*pb.y+c1>0)
return pa.s>pb.s;
else
return a*pa.x+b*pa.y+c1<a*pb.x+b*pb.y+c1?0:1;
}
int px1(O pa, O pb)
{
return pa.s<pb.s;
}
int main()
{
scanf("%lld%lld%lld%lld%lld",&n, &a, &b, &c1, &c2);
for(int i=0;i<n;i++)
{
scanf("%lf%lf%lf",&w[i].x, &w[i].y, &w[i].r);
};
if(c1>c2)
swap(c1, c2);
for(int i=0;i<n;i++)//初始化
{
double fz=fabs(a*w[i].x+b*w[i].y+c1);
double fm=sqrt(a*a+b*b);
double f1=fz/fm;
if(f1<=w[i].r)
w[i].s=0;
else
w[i].s=f1-w[i].r;
}
sort(w,w+n, px);//圆的排序
for(int i=0;i<n;i++)//dp递推
{
for(int j=i+1;j<n;j++)
{
double s1=sqrt((w[i].x-w[j].x)*(w[i].x-w[j].x)+(w[i].y-w[j].y)*(w[i].y-w[j].y));
double s2=w[i].r+w[j].r;
if(s1>s2)
w[j].s=min(w[i].s+s1-s2, w[j].s);
else
w[j].s=min(w[i].s, w[j].s);
}
}
for(int i=0;i<n;i++)//加上到L2的距离
{
double fz=fabs(a*w[i].x+b*w[i].y+c2);
double fm=sqrt(a*a+b*b);
double f1=fz/fm;
if(f1<=w[i].r)
w[i].s+=0;
else
w[i].s+=f1-w[i].r;
}
sort(w,w+n, px1);//按s从小到大排序
printf("%.6f\n",w[0].s);
return 0;
}