[手写hash][bfs]扫雷 2022年蓝桥杯

3 篇文章 0 订阅
2 篇文章 0 订阅

题目描述

小明最近迷上了一款名为《扫雷》的游戏。

其中有一个关卡的任务如下:

在一个二维平面上放置着 n 个炸雷,第 i 个炸雷 (xi,yi,ri) 表示在坐标 (xi,yi) 处存在一个炸雷,它的爆炸范围是以半径为 ri 的一个圆。

为了顺利通过这片土地,需要玩家进行排雷。

玩家可以发射 m 个排雷火箭,小明已经规划好了每个排雷火箭的发射方向,第 j 个排雷火箭 (xj,yj,rj) 表示这个排雷火箭将会在 (xj,yj) 处爆炸,它的爆炸范围是以半径为 rj 的一个圆,在其爆炸范围内的炸雷会被引爆。

同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。

现在小明想知道他这次共引爆了几颗炸雷?

你可以把炸雷和排雷火箭都视为平面上的一个点。

一个点处可以存在多个炸雷和排雷火箭。

当炸雷位于爆炸范围的边界上时也会被引爆。

输入格式

输入的第一行包含两个整数 n、m。

接下来的 n 行,每行三个整数 xi,yi,ri,表示一个炸雷的信息。

再接下来的 m 行,每行三个整数 xj,yj,rj,表示一个排雷火箭的信息。

输出格式

输出一个整数表示答案。

数据范围

对于 40% 的评测用例:0≤x,y≤10^9,0≤n,m≤10^3,1≤r≤10,
对于 100% 的评测用例:0≤x,y≤10^9,0≤n,m≤5×10^4,1≤r≤10。

输入样例:

2 1
2 2 4
4 4 2
0 0 5

输出样例:

2

样例解释

示例图如下,排雷火箭 1 覆盖了炸雷 1,所以炸雷 1 被排除;炸雷 1 又覆盖了炸雷 2,所以炸雷 2 也被排除。

题意: 有n颗地雷以及m个排雷火箭,分别给出这n+m个点的坐标以及其爆炸半径,处于爆炸半径内的地雷都会被引爆,这个反应可以连锁传递下去,开始时依次引爆所有排雷火箭,问最终能引爆多少颗地雷。

分析: 这道题目有个很关键的信息,所有点爆炸半径都不超过10,而且每个点坐标都是整数,那么对于一个点其半径内的点完全可以枚举出来,这样最多也才枚举不到400个点而已。于是可以以点坐标为索引,存储该点的最大半径以及该点雷数,一开始我用的是map,但是一直T,后来听y总说这题必须手写hash,于是换上手写hash,将点的坐标映射到一个小于1e6的整数上,然后存储点信息的数组都可以以该整数为索引了。手写hash的过程就和数据结构书上写的一样,首先先将点坐标看作一个1e9+1进制的数字,然后将该数字映射到哈希数组上,哈希函数构造方法为除留余数法,冲突处理方法为线性探测再散列。

得到某点的最大半径以及雷数就可以bfs或dfs了,后面的步骤就比较常规了,这道题目主要是手写hash比较难想,根本想不到会卡常数。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <queue>
#define int long long
#define pii pair<int, int>
using namespace std;

const int mod = 1000003;//哈希表表长 
int n, m;
int x[100005], y[100005], r[100005];
int h[1000005];
bool vis[1000005];
pii mp[1000005];//记录该点处最大半径及雷数 

int get_key(int xx, int yy){//转化为一个1e9+1进制的数 
	return xx*1000000001+yy;
}

int find(int xx, int yy){//将一个二维坐标映射到小于mod的数字 
	int key = get_key(xx, yy);
	int temp = key;
	temp = (temp%mod+mod)%mod;
	while(!(h[temp] == -1 || h[temp] == key))
		temp = (temp+1)%mod;
	return temp;
} 

signed main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	for(int i = 1; i <= n; i++){
		scanf("%lld%lld%lld", &x[i], &y[i], &r[i]);
		h[find(x[i], y[i])] = get_key(x[i], y[i]);
		int id = find(x[i], y[i]);
		mp[id].second++;
		mp[id].first = max(mp[id].first, r[i]);
	}
	for(int i = n+1; i <= n+m; i++){
		scanf("%lld%lld%lld", &x[i], &y[i], &r[i]);
		h[find(x[i], y[i])] = get_key(x[i], y[i]);
		int id = find(x[i], y[i]);
		mp[id].first = max(mp[id].first, r[i]);
	}
	queue<pii> q;
	for(int i = n+1; i <= n+m; i++){
		int id = find(x[i], y[i]);
		if(!vis[id]){
			vis[id] = true;
			q.push(make_pair(x[i], y[i]));
		}
	}
	int res = 0;
	while(q.size()){
		pii now = q.front();
		q.pop();
		int nowx = now.first, nowy = now.second;
		int id = find(nowx, nowy);
		int rmax = mp[id].first, num = mp[id].second;
		res += num;
		for(int xx = nowx-rmax; xx <= nowx+rmax; xx++)
			for(int yy = nowy-rmax; yy <= nowy+rmax; yy++)
				if(rmax*rmax >= (xx-nowx)*(xx-nowx)+(yy-nowy)*(yy-nowy)){
					int id = find(xx, yy);
					if(!vis[id] && mp[id].second){//如果该处有雷且未被访问过
						vis[id] = true;
						q.push(make_pair(xx, yy)); 
					}
				}
	}
	cout << res;
    return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值