2022 8.22 T2 刺客信条(kill.cpp)
T2 刺客信条(kill.cpp)
题目描述
【问题描述】
故事发生在 1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵
族,后来因为家族成员受到圣殿骑士的杀害,决心成为一名刺客。最终,
凭借着他的努力和出众的天赋,成为了杰出的刺客大师。刺客组织在他的
带领下,为被剥削的平民声张正义,赶跑了原本统治意大利的圣殿骑士首
领-教皇亚历山大六世。在他的一生中,经历了无数次惊心动魄、扣人心弦
的探险和刺杀。
这次的故事就是他暗杀一位作恶多端的红衣主教。红衣主教可以吸取
他周围人的生命力量(以他为圆心的一个圆),而他的红衣教徒也拥有这个
力量。红衣主教的家是一个 x*y 的长方形房间,也就是说,他的家的四个
角坐标分别为(0,0),(x,0),(0,y),(x,y)。教堂的门在(0,0) ,而红衣主教
就在(x,y)的卧室休息。他的家中还有 n 个守护着他的红衣教徒,站在
(ai,bi)。Ezi 想要趁主教休息时,从门进入潜入到他的卧室刺杀他,因为
主教休息时会脱下红衣,这样吸取生命的力量就消失了。可是守卫他的红
衣教徒依然很危险,离红衣教徒太近就会被吸取生命。因此,Ezi 想知道,
当红衣教徒的影响范围最大为多少时,Ezi 可以不被吸取生命并且成功的刺
杀掉红衣主教(当两个红衣教徒的影响范围相切时,可以视为 Ezio 恰好可
以穿过)。注意:教徒都在房间里。
【输入格式】
第一行三个整数 x,y,n。之后 n 行,每行两个整数 ,意义见题目描述。
【输出格式】
一行一个数 D,表示红衣教徒的最大影响范围,保留两位小数。
【样例输入】
10 20 2
3 3
6 14
【样例输出】
3.00
【样例解释】
贴着墙走
【数据范围】
对于 20%的数据,n<=10;
对于 40%的数据,n<=100;
对于 100%的数据,n<=2000,0<=x,y<=10^6。
官方题解
二分,点变成了圆。问是否能够存在一条路径,不碰到圆,从左下角走到右上角。
比赛的时候卡了我 2 个小时,严重影响节奏啊!!!!!
并查集。
如果有交点,那么直接圆/边归入一集。
然后判断一下 4 条边是否在一个集合即可。
看不懂上面题解的同学,转百度:JZOJ 5904
再讲解
上面讲的相当不清楚啊。。。
二分这个大家都能看出来对吧(考场上一眼得出)
但判定怎么搞呢?(实数域二分本来就恶心)
答案:并查集求交
思路非常巧妙。实际上我们求的不是交,而是:
1.把所有能封锁左边墙和上边墙的点放入一个集合
2.类似地,把所有能封锁下边或右边墙的点放入一个集合
3.类似地,把所有两两之间会被封锁的点加入一个并查集
4.枚举两集合中的点,若有分属于两集合的不同点在同一并查集内,则认定答案不可行,否则若不存在这样的点对,则答案合法
!提示
- 若两点在同一并查集内,则他们一定直接或间接地连成一堵刺客穿不过的墙
- 可以列举证明,若一堵链接成的墙连在左、上方的墙上,又连在右、下方的墙上,则他们一定把刺客的路堵死
我的思路
也是二分法 写挂了 QwQ ToT
Std
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int xi,yi;
};
node a[2010];
int num1[2010],num2[2010];
int f[2010];
int x,y,t;
int getf(int s) {
if(s==f[s])
return s;
else
return f[s]=getf(f[s]);
}
bool check(double s) {
int cnt1=0;
int cnt2=0;
for(int i=1; i<=t; i++) {
f[i]=i;//初始化并查集
if(a[i].xi+s>=x||a[i].yi-s<=0) { //左下角的点
cnt1++;
num1[cnt1]=i;
//f[i]=-1;//把左下角的点放入一个并查集
}
if(a[i].xi-s<=0.0||a[i].yi+s>=y) { //右上角的点
cnt2++;
num2[cnt2]=i;
// if(f[i]==-1)
// return 1;//有点把两边都连通了
// f[i]=-2;//把右上角的点放入一个并查集
}
}
for(int i=1; i<=t; i++) {
for(int j=i+1; j<=t; j++) {
double t1=a[i].xi*1.0-a[j].xi;
double t2=a[i].yi*1.0-a[j].yi;
if(f[i]==f[j])
continue;
if(t1*t1+t2*t2<=4*s*s) { //可以连通
int tp1=getf(i);
int tp2=getf(j);
if(tp1!=tp2)
f[tp1]=tp2;
}
}
}
for(int i=1; i<=cnt1; i++) {
for(int j=1; j<=cnt2; j++) {
if(getf(f[num1[i]])==getf(f[num2[j]]))
return 1;
}
}
return 0; //如果执行到这里都还没结束,明显就不能分割
}
int main() {
freopen("kill.in","r",stdin);
freopen("kill.out","w",stdout);
scanf("%d%d%d",&x,&y,&t);
for(int i=1; i<=t; i++) {
scanf("%d%d",&a[i].xi,&a[i].yi);
}
double l=0;
double r=1000000;
while(r-l>1e-4) {
double mid=(r+l)/2;
if(check(mid)) {
r=mid;
} else {
l=mid;
}
}
printf("%.2lf",r);
return 0;
}
本文来自博客园,作者:haozexu,转载请注明原文链接:https://www.cnblogs.com/haozexu/p/18281797

浙公网安备 33010602011771号