【题解】袭击

题目链接

题目描述

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

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

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

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

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

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

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

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

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

输入格式

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

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

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

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

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

输出格式

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

每个输出结果占一行。

样例
样例输入
2
4
0 0
0 1
1 0
1 1
2 2
2 3
3 2
3 3
4
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
样例输出
1.414
0.000
数据范围与提示

1 ≤ N ≤ 100000 1\le N\le100000 1N100000

0 ≤ X , Y ≤ 1000000000 0\le X,Y\le1000000000 0X,Y1000000000

Solution

分治。(不要问我怎么知道的)

铺垫 题解

好了,等你看完上面的部分,这题最妙的部分就不用我讲了,我就没事干了,本文终结。

对于同一个分组,它的最近点对我们可以这样求解:

将它按 x x x 优先, y y y 其次的规则从小到大排序,凡是看到关于坐标的不简单题,是个人都会先把 sort ⁡ \operatorname{sort} sort 写上。可惜我不是人

那么,现在这个数组已经从左到右排好了,我们把它分成两半截。开始盗图

假设我们已经求出来了 [ l e f t , m i d ] [left,mid] [left,mid] 的答案和 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right] 的答案,我们用 d d d (也就是记录答案的变量)记录它们的 min ⁡ \min min 值。

但四!可爱的孩纸们,难道距离最近的就不可能在中间那一段吗?

在上图中:

  • 蓝色+紫色为答案可能区间1( [ l e f t , m i d ] [left,mid] [left,mid] )
  • 紫色+橙色为答案可能区间2( 将求的值 )
  • 橙色+绿色为答案可能区间3( [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right] )

那么,区间2如何处理呢?

上图中,紫色和橙色长方形的宽都是 d d d,为什么呢?因为只有当它们的距离在 d d d 范围内, d d d 才有可能被更新,所以我们只用计算 l l l ~ r r r 中, x x x 坐标在 [ m i d − d , m i d + d ] [mid-d,mid+d] [midd,mid+d] 范围内的点就行啦!

把满足上述要求的点放在 t m p tmp tmp 数组中,由于这个数组本身已经在范围内了,此时我们按 y y y 坐标排序,这样便于找到与某个点距离最近的点。

枚举 t m p tmp tmp 中的每一对点。当这一对点的 y y y 坐标的距离大于 d d d 时,我们就可以 b r e a k break break 掉了。因为已经按 y y y 坐标排好序, y y y 小的点太远,更大的自然不行。

但,这只是同一个分组的情况。不同分组其实也没有什么不同,就是在求距离的时候判一下就行了。

综上所述。。。

Code

#include<cmath>
#include<cfloat>
#include<cstdio>
#include<algorithm>
using std::sort;
typedef double db;
const int maxn=1e5+5;
const db inf=DBL_MAX;
struct node{
	db x,y;
	bool type;
	db operator-(const node q)const{
		if(type==q.type)return inf;
		return sqrt((x-q.x)*(x-q.x)+(y-q.y)*(y-q.y));
	}
}a[maxn],tmp[maxn];
int T,n;
bool cmpx(node x,node y){
	return x.x==y.x?x.y<y.y:x.x<y.x;
}
bool cmpy(node x,node y){
	return x.y==y.y&&x.x<y.x; //玄学cmp
}
db min(db x,db y){
	return x<y?x:y;
}
db max(db x,db y){
	return x>y?x:y;
}
db ll45l4(int l,int r){ //恶臭函数名
	if(r-l<=1)return a[r]-a[l];
	int mid=l+r>>1,cnt=0;
	db d=min(ll45l4(l,mid),ll45l4(mid+1,r));
	for(int i=l;i<=r;++i){
		if(a[mid].x-d<=a[i].x&&a[mid].x+d>=a[i].x)
			tmp[++cnt]=a[i]; 
	}
	sort(tmp+1,tmp+cnt+1,cmpy);
	for(int i=1;i<=cnt;++i){
		for(int j=i+1;j<=cnt;++j){
			if(a[j].y-a[i].y>d)break;
			d=min(d,a[i]-a[j]);
		}
	}
	return d;
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=1;i<=n;++i){
			scanf("%lf%lf",&a[i].x,&a[i].y);
			a[i].type=0;
		}
		for(int i=1;i<=n;++i){
			scanf("%lf%lf",&a[n+i].x,&a[n+i].y);
			a[i].type=1;
		}
		n<<=1;
		sort(a+1,a+n+1,cmpx);
		printf("%.3lf\n",ll45l4(1,n));
	}
	return 0;
}

end.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
06-01
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值