JZOJ 5904. 【NOIP2018模拟10.15】刺客信条

题意

给定一个长方形,长方形内有一些点。问从左下角走到右上角,离那些点的最远距离是多少。

解题思路

虽然简单,但想了很久。
二分,点变成了圆。问是否能够存在一条路径,不碰到圆,从左下角走到右上角。
比赛的时候卡了我2个小时,严重影响节奏啊!!!!!
并查集。
如果有交点,那么直接圆/边归入一集。
然后判断一下4条边是否在一个集合即可。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 2010 
#define DB double
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
	DB x,y;
}a[N];
int i,j,k,l,n,m;
int f[N],st,en;
DB x,y,L,R,Mid,ans,eps;
DB MIN;
int read(){
	int rs=0,fh=1;char ch;
	while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
	if(ch=='-')fh=-1,ch=getchar();
	while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
	return rs;
}
DB dis(DB X1,DB X2,DB Y1,DB Y2){
	return (X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2);
}
int get(int x){
	return f[x]==x?x:f[x]=get(f[x]);
}
void merge(int x,int y){
	int gx=get(x),gy=get(y);
	if(gx^gy)f[gy]=gx;
}
bool check(DB Mid){
	int i,j;
	fo(i,1,n+4)f[i]=i;
	fo(i,1,n){
		if(a[i].x<Mid)merge(n+1,i);
		if(x-a[i].x<Mid)merge(n+2,i);
		if(a[i].y<Mid)merge(n+3,i);
		if(y-a[i].y<Mid)merge(n+4,i); 
	}
	fo(i,1,n-1)fo(j,i+1,n){
		if(dis(a[i].x,a[j].x,a[i].y,a[j].y)<4*Mid*Mid){
			merge(i,j);
		}
	}
	f[n+1]=get(f[n+1]);
	f[n+2]=get(f[n+2]);
	f[n+3]=get(f[n+3]);
	f[n+4]=get(f[n+4]);
	if(f[n+1]==f[n+2]||f[n+1]==f[n+3])return 0;
	if(f[n+2]==f[n+4]||f[n+4]==f[n+3])return 0;
	return 1;
}
int main(){
	scanf("%lf%lf%d",&x,&y,&n);
	fo(i,1,n){
		k=read(),l=read();
		a[i].x=1.0*k;a[i].y=1.0*l;
	}
	eps=1e-4;
	L=0,R=min(x,y)/2.0;ans=0;
	while(R-L>eps){
		Mid=(L+R)/2.0;
		if(check(Mid))ans=Mid,L=Mid+eps;
		else R=Mid-eps;
	}
	printf("%.2lf",ans);
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值