1. 程式人生 > >牛客國慶集訓派對Day1 New Game!+計算幾何

牛客國慶集訓派對Day1 New Game!+計算幾何

題目連結: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;
}