POJ 3714 Raid

题目大意:

        现空头N个特工到敌方阵营去破坏其发电站,现有N个特工和N个发电站(1 ≤ N ≤ 100,000),由于地方空军阻拦等原因特工没有按照预定地点降落,因此降落地点非常散乱,现需要你计算力发电站最近的一个特工以及这个最近距离。

        现有多个测例(测例数题中给出),每个测例中都给出N,接着会给出N个发电站的二维坐标和N个特工的坐标,所有坐标的范围为[0, 1000000000],所有的输入都是int型的,对于每个测例求出最近两点之间的距离(这两点分别是发电站和特工,不能是相同的东西),要求结果精确到小数点后3位,有可能有特工刚好降落到某个发电站的情况,这时最短距离就是0。

题目链接

注释代码:

/*       
 * Problem ID : POJ 3714 Raid
 * Author     : Lirx.t.Una       
 * Language   : C++      
 * Run Time   : 2844 ms       
 * Run Memory : 4956 KB       
*/

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>

#define	EPS		1E-9
#define	INF		1E50

#define	STATION		true
#define	AGENT		false

//最大点数
//由于station和agent具有同样的最大数量
//因此为它们最大数量的两倍
#define	MAXPOINTN		200000
//在求最近点对的合并一部中需要确定一个
//最小的边界合并区域
//YN即为该区域(只考虑水平范围)的最大点数
//只有要对该区域中的点按照y升序排列
#define	YN				190

#define	POW(x)		( (x) * (x) )

using namespace std;

struct	Point {

	double	x, y;
	bool	type;//表示是station还是agent

	friend istream &//输入
	operator>>( istream &is, Point &p ) {
	
		is >> p.x >> p.y;

		return is;
	}

	double
	operator^(Point	&oth) {//求两点之间的欧式距离
	
		if ( type != oth.type )
			return sqrt( POW( x - oth.x ) + POW( y - oth.y ) );
		else//如果是两个相同类型的点则不可达,因此距离为无穷大
			return INF;
	}
};

Point	pt[MAXPOINTN];//点
int		ys[YN];//y-sort,保存边界区域中的点的序号,之后要按照y升序排列

bool
xfcmp(const Point &p1, const Point &p2) {//对所有点按照x升序排列
	//以求得x的中位数可以进行二分分治
	//使该中垂线两边的点的数量相等或相差1

	return p1.x < p2.x;
}

bool
yfcmp(int i, int j) {//对边界区域中的点进行y排序

	return pt[i].y < pt[j].y;
}

double
nirst( int lft, int rht ) {//Nearest pair
	//分治递归求最近点对之间的距离

	if ( 1 == rht - lft )//只有两个点
		return pt[lft] ^ pt[rht];

	int		mid;

	if ( 2 == rht - lft ) {//只有三个点
	
		mid = lft + 1;
		return min( min( pt[lft] ^ pt[mid], pt[mid] ^ pt[rht] ), 
				    pt[lft] ^ pt[rht] );
	}

	double	mdist;//minimum distance,当前的最近点对距离,会不断覆盖

	mid   = ( lft + rht ) >> 1;//求中垂线,即x的中位数,之前已经把pt按照x升序排列了
	mdist = min( nirst( lft, mid ), nirst( mid + 1, rht ) );//分治求两边的最近点对
	if ( fabs(mdist) < EPS )//表示有重合station和agent,可以直接输出0.0的距离
		return 0.0;

	//接下来就是求两边交界的边界合并区中的最近点对了
	//边界区域是中垂线两侧宽度为mdist的区域(总宽度为2mdist)
	int		ym;//区域中点的数量
	int		i, j;//计数变量

	//向两边纵深,只找出里中垂线mdist以内的点即可
	for ( ym = 0, i = mid; pt[mid].x - pt[i].x < mdist && i >= lft; i-- )
		ys[ym++] = i;
	for ( i = mid + 1; pt[i].x - pt[mid + 1].x < mdist && i <= rht; i++ )
		ys[ym++] = i;
	sort(ys, ys + ym, yfcmp);//对区域中的点按照y升序排列
	
	//由于在 2mdist × mdist的区域中最多只有8个点,因此在边界区域中
	//最多只有 8 × ( ( ymax - ymin ) / mdist )个点
	//因此点数还是比较少的,因此可以枚举两两之间的距离
	for ( i = 0; i < ym; i++ )
		for ( j = i + 1;
		        //剪枝!!!利用不等式放缩剪枝,如果y之差都大于mdist了
				//则两点之间距离肯定大于mdist(直角三角形斜边大于直角边)
		        j < ym && pt[ ys[j] ].y - pt[ ys[i] ].y < mdist;
		          j++ )
			mdist = min( mdist, pt[ ys[i] ] ^ pt[ ys[j] ] );
		
	return mdist;
}

int
main() {
	
	int		t;//测例数
	int		n;//station和agent的数量
	int		nn;//前两者的综合
	
	int		i;//技术变量
	
	scanf("%d", &t);
	while ( t-- ) {
		
		scanf("%d", &n);
		nn = n << 1;//nn = 2n
		for ( i = 0; i < n; i++ ) {
			
			cin >> pt[i];
			pt[i].type = STATION;
		}
		for ( i = n; i < nn; i++ ) {
			
			cin >> pt[i];
			pt[i].type = AGENT;
		}
		sort(pt, pt + nn, xfcmp);
		
		printf("%.3lf\n", nirst( 0, nn - 1 ));
	}
	
	return 0;
}

无注释代码:

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>

#define	EPS		1E-9
#define	INF		1E50

#define	STATION		true
#define	AGENT		false

#define	MAXPOINTN		200000
#define	YN				190

#define	POW(x)		( (x) * (x) )

using namespace std;

struct	Point {

	double	x, y;
	bool	type;

	friend istream &
	operator>>( istream &is, Point &p ) {
	
		is >> p.x >> p.y;

		return is;
	}

	double
	operator^(Point	&oth) {
	
		if ( type != oth.type )
			return sqrt( POW( x - oth.x ) + POW( y - oth.y ) );
		else
			return INF;
	}
};

Point	pt[MAXPOINTN];
int		ys[YN];

bool
xfcmp(const Point &p1, const Point &p2) {

	return p1.x < p2.x;
}

bool
yfcmp(int i, int j) {

	return pt[i].y < pt[j].y;
}

double
nirst( int lft, int rht ) {

	if ( 1 == rht - lft )
		return pt[lft] ^ pt[rht];

	int		mid;

	if ( 2 == rht - lft ) {
	
		mid = lft + 1;
		return min( min( pt[lft] ^ pt[mid], pt[mid] ^ pt[rht] ), 
				    pt[lft] ^ pt[rht] );
	}

	double	mdist;

	mid   = ( lft + rht ) >> 1;
	mdist = min( nirst( lft, mid ), nirst( mid + 1, rht ) );
	if ( fabs(mdist) < EPS )
		return 0.0;

	int		ym;
	int		i, j;

	for ( ym = 0, i = mid; pt[mid].x - pt[i].x < mdist && i >= lft; i-- )
		ys[ym++] = i;
	for ( i = mid + 1; pt[i].x - pt[mid + 1].x < mdist && i <= rht; i++ )
		ys[ym++] = i;
	sort(ys, ys + ym, yfcmp);
	
	for ( i = 0; i < ym; i++ )
		for ( j = i + 1;
		        j < ym && pt[ ys[j] ].y - pt[ ys[i] ].y < mdist;
		<span style="white-space:pre">	</span>  j++ )
		<span style="white-space:pre">	</span>mdist = min( mdist, pt[ ys[i] ] ^ pt[ ys[j] ] );
		
	return mdist;
}

int
main() {
	
	int		t;
	int		n;
	int		nn;
	
	int		i;
	
	scanf("%d", &t);
	while ( t-- ) {
		
		scanf("%d", &n);
		nn = n << 1;
		for ( i = 0; i < n; i++ ) {
			
			cin >> pt[i];
			pt[i].type = STATION;
		}
		for ( i = n; i < nn; i++ ) {
			
			cin >> pt[i];
			pt[i].type = AGENT;
		}
		sort(pt, pt + nn, xfcmp);
		
		printf("%.3lf\n", nirst( 0, nn - 1 ));
	}
	
	return 0;
}

单词解释:

raid:n, 突袭

retreat:vt, 撤退

stronghold:n, 要塞,大本营

repel:vt, 击退

sleepless:adj, 失眠的,不眠的

Arthur:人名,亚瑟

General:n, 将军,上将

nuclear:adj, 原子能的

disable:vt, 使残废

agent:n, 特工,代理人

paradrop:vt, 空投,伞投

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值