NOIP备战题解集(11.10)

A.袭击(平面最近点对)

题目描述
在与联盟的战斗中屡战屡败后,帝国撤退到了最后一个据点。

依靠其强大的防御系统,帝国击退了联盟的六波猛烈进攻。

经过几天的苦思冥想,联盟将军亚瑟终于注意到帝国防御系统唯一的弱点就是能源供应。

该系统由N个核电站供应能源,其中任何一个被摧毁都会使防御系统失效。

将军派出了N个特工进入据点之中,打算对能源站展开一次突袭。

不幸的是,由于受到了帝国空军的袭击,他们未能降落在预期位置。

作为一名经验丰富的将军,亚瑟很快意识到他需要重新安排突袭计划。

他现在最想知道的事情就是哪个特工距离其中任意一个发电站的距离最短。

你能帮他算出来这最短的距离是多少吗?

输入格式
输入中包含多组测试用例。

第一行输入整数T,代表测试用例的数量。

对于每个测试用例,第一行输入整数N。

接下来N行,每行输入两个整数X和Y,代表每个核电站的位置的X,Y坐标。

在接下来N行,每行输入两个整数X和Y,代表每名特工的位置的X,Y坐标。

输出格式
每个测试用例,输出一个最短距离值,结果保留三位小数。

每个输出结果占一行。

solution:

分治+二分(最近点对问题)
算法分析:这是一道经典的最近点对问题的模板,这里略微讲述一下这种问题的解法,首先呢,我们将这些点的 x x x坐标,为第一关键字, y y y坐标为第二关键字,从小到大排序,接着我们取一个中点 m i d mid mid点,将 m i d mid mid点左边的点,统统归为平面 d 1 d1 d1,然后 m i d mid mid点右边的点统统归为平面 d 2 d2 d2如下图所示。

在这里插入图片描述
然后在如图所示的左右均 d / 2 d/2 d/2区域中,再按 y y y为关键字排序。再暴力枚举。注意可以剪枝。

时间复杂度是 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn)。优化主要是在跨区间的寻找上,可以省掉不少的枚举时间。
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int inf=0x3f3f3f3f;
inline int read()
{
   
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {
   if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {
   X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
struct node {
   
	double x,y;
	int f;
}a[N],b[N],t[N];
double dist(int l,int r) {
   
	if(a[l].f==a[r].f) return inf;
	return sqrt((a[l].x-a[r].x)*(a[l].x-a[r].x)+(a[l].y-a[r].y)*(a[l].y-a[r].y));
}
int T,n;
bool cmp(node A,node B) {
   
	return A.y<B.y;
}
bool cmp2(node A,node B) {
   
	return A.x<B.x;
}
double dfs(int l,int r) {
   
	if(l+1>=r) return inf;
	int mid=(l+r)>>1,cnt=0;
	double res=min(dfs(l,mid),dfs(mid,r));
	int i=l,j=mid,num=l;
	while(i<mid||j<r) {
   
		if(i==mid) b[num++]=a[j++];
		else if(j==r) b[num++]=a[i++];
		else if(a[i].y<a[j].y) b[num++]=a[i++];
		else b[num++]=a[j++];
	}
	for(int i=l;i<=r;i++) a[i]=b[i];
	for(int i=mid-1;i>=l;i--)
	    if(a[mid].x-a[i].x<res)
	        t[++cnt]=a[i];
	for(int i=mid;i<r;i++) 
	    if(a[i].x-a[mid].x<res)
	        t[++cnt]=a[i];
	for(int i=1;i<=cnt;i++)
	    for(int j=i+1;j<=cnt&&t[j].y-t[i].y<res;j++) {
   
	    	if(t[i].f==t[j].f) continue;
	    	res=min(res,sqrt((t[i].x-t[j].x)*(t[i].x-t[j].x)+(t[i].y-t[j].y)*(t[i].y-t[j].y)));
		}
	return res;
}
signed main() {
   
    T=read();
    while(T--) {
   
    	n=read();
    	for(int i=1;i<=n;i
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值