洛谷 1783 海滩防御 题解

题意简述

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

思路简述

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

具体思路

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

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

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

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

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

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

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#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;
}
w