洛谷 1783 海滩防御 题解

博客观赏效果更佳

题意简述

平面直角坐标系上有 m ( < = 800 ) m(<=800) 个点,每个点的 x x 坐标都在 [ 1 , n ] [1,n] 之内, n < = 1000 n<=1000 y y 坐标 < = 1 e 5 <=1e5 。每个点珂以设一个半径为 r r 的攻击塔(攻击范围包含边界)。 x x 轴上 [ 1 , n ] [1,n] 的区域是敌人的进攻线,请你在每个位置都设置一些防御塔,使得敌人无法在 y y 坐标上无限增大(敌人珂以各种绕路),并且 r r 的最大值最小。

思路简述

抽象成图。边权为两个点之间的距离/2。设置起点S,S到一个点的距离为这个点的 x x 坐标。设置终点T,T到一个点的距离为n-这个点的 x x 坐标。从S到T跑最短路,路径的长度定义为路径上所有边权的最大值。

具体思路

如果我们的最大半径为 r r ,那么肯定每个点都要设置半径 r r 。反正半径又不花钱,那肯定往大里放啊。

然后对于一个点 u , v u,v ,设距离为 d d 。那么,两边都设置半径为 d / 2 d/2 就能覆盖 u , v u,v 之间的部分了。

当然,我们的目的是封锁 y y 轴。稍微转化一下,就是要从左到右完美的封锁出“一条”来。“一条”珂以是曲线,看起来还很粗,只要不空出来即珂。因为敌人走位随便走,你只要一点空出来,敌人钻过去,就珂以无限向 y y 轴延伸了。

所以,我们还要建立两个点,表示左边界和右边界。就是上面说的S和T。要说几何意义,就是S:直线x=0,T:直线x=n。然后S到一个点显然需要半径为这个点的x坐标值,才能封锁这个点到S的范围。T同理。

然后,对于我们从S到T,也就是从左到右,选择的一条链上,一个空位也不能有,也就是每个空都要完美的守住。所以这条链的长度就是最长边的值。

这样跑一遍最短路即珂。记得边权是实数,而不是整数。

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define real double
    #define N 1333
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    class Graph
    {
        public:
            int head[N*N];
            int EdgeCount;
            struct Edge
            {
                int To;real Label;int Next;
            }Ed[N*N<<1];
            void clear(int _V=N,int _E=N<<1) 
            {
                memset(Ed,-1,sizeof(Edge)*(_E));
                memset(head,-1,sizeof(int)*(_V));
                EdgeCount=-1;
            }
            void AddEdge(int u,int v,real w=0.0)
            {
                Ed[++EdgeCount]=(Edge){v,w,head[u]};
                head[u]=EdgeCount;
            }
            void Add2(int u,int v,real w=1) {AddEdge(u,v,w);AddEdge(v,u,w);}
            int Start(int u) {return head[u];}
            int To(int u){return Ed[u].To;}
            real Label(int u){return Ed[u].Label;}
            int Next(int u){return Ed[u].Next;}
    }G;
    
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    int n,m;
    real x[N],y[N];
    void Input()
    {
        R1(n),R1(m);
        F(i,1,m) scanf("%lf%lf",&x[i],&y[i]);
    }

        
    real d(int dx,int dy){return sqrt(dx*dx+dy*dy);}//求距离
    struct node{int v;real w;};bool operator<(node a,node b){return a.w>b.w;}
    bool vis[N];real dis[N];
    priority_queue<node> Q;
    void Dijkstra(int s)
    {
        F(i,1,m+2) dis[i]=1e18;
        Q.push((node){s,0.00});dis[s]=0.00;
        while(!Q.empty())
        {
            node Min=Q.top();Q.pop();
            int u=Min.v;
            if (vis[u]) continue;
            vis[u]=1;

            Tra(i,u)
            {int v=__v;
                if (dis[v]>max(dis[u],G.Label(i)))
                {
                    dis[v]=max(dis[u],G.Label(i));
                    Q.push((node){v,dis[v]});
                }
            }
        }
    }//Dijkstra算法(堆优化)
    void Soviet()
    {
        int s=m+1,t=m+2;
        G.clear();
        F(i,1,m)
        {
            G.Add2(i,s,x[i]);
            G.Add2(i,t,(n-x[i]));
        }//S和T的边
        F(i,1,m) F(j,1,i-1) 
        {
            G.Add2(i,j,d(x[i]-x[j],y[i]-y[j])/2);
        }//两点之间的边,边权为距离/2
        Dijkstra(s);
        printf("%.2f\n",dis[t]);
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
发布了210 篇原创文章 · 获赞 8 · 访问量 8989

猜你喜欢

转载自blog.csdn.net/LightningUZ/article/details/103338678