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;
}
posted @ 2022-08-22 20:20  haozexu  阅读(34)  评论(0)    收藏  举报  来源