炸雷!(地址映射+dfs搜索)

小明最近迷上了一款名为《扫雷》的游戏。其中有一个关卡的任务如下, 在一个二维平面上放置着 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,表示一个排雷火箭的信息。

输出格式

输出一个整数表示答案。

样例输入

复制代码
2 1
2 2 4
4 4 2
0 0 5

样例输出

复制代码
2

提示

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

对于 40% 的评测用例:0 ≤ x, y ≤ 1e9 , 0 ≤ n, m ≤ 1e3 , 1 ≤ r ≤ 10.

对于 100% 的评测用例:0 ≤ x, y ≤ 1e9 , 0 ≤ n, m ≤ 5 × 1e4 , 1 ≤ r ≤ 10.

这个问题是一个典型的圆覆盖与连锁反应 问题,本质上可以抽象为图的遍历(DFS/BFS)

核心挑战

  1. 连锁反应:火箭引爆炸雷,炸雷再引爆炸雷。这构成了一个有向图的搜索问题。

  2. 坐标范围巨大:x, y高达 10^9,无法使用二维数组。

  3. 圆心距离计算:判断点 (x1, y1) 是否在半径为 r 的圆 (x0, y0, r) 内,公式为:

    (x1-x0)^2+(y1-y0)^2<=r^2

  4. 爆炸半径很小 :注意到提示中 r ≤ 10.,这是一个非常关键的突破口。

优化思路:哈希表 + 邻域搜索

由于坐标极大但半径很小,我们可以将所有炸雷的坐标通过**哈希(Hash)**存储。

  • 存储 :使用 std::mapstd::unordered_map 将坐标 (x, y) 映射到该位置上的炸雷信息(记录半径和数量)。为了处理哈希冲突和坐标组合,通常将 xy 转化成一个唯一键,例如 key = x * OFFSET + y

  • 搜索范围 :对于一个爆炸圆 (x, y, r),我们不需要遍历所有 n个炸雷。由于 r ≤ 10.,我们只需要搜索 x 坐标在 [x-r, x+r] 且 y 坐标在 [y-r, y+r] 范围内的点即可。

  • 去重:一旦某个坐标上的炸雷被引爆,就标记为已访问,防止死循环。

#include <iostream>

#include <vector>

#include <unordered_map>

#include <cmath>

using namespace std;

typedef long long LL;

// 炸雷结构体

struct Mine {

int x, y, r, num;

bool visited;

};

// 哈希函数,将坐标映射为唯一 key

const LL OFFSET = 1000000001LL;

unordered_map<LL, int> pos_to_id;

vector<Mine> mines;

// 距离平方计算

LL get_dist_sq(LL x1, LL y1, LL x2, LL y2) {

return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);

}

int ans = 0;

void dfs(int id) {

mines[id].visited = true;

ans += mines[id].num;

LL x = mines[id].x;

LL y = mines[id].y;

LL r = mines[id].r;

// 只搜索半径范围内的格子

for (LL i = x - r; i <= x + r; i++) {

for (LL j = y - r; j <= y + r; j++) {

LL key = i * OFFSET + j;

if (pos_to_id.count(key)) {

int next_id = pos_to_id[key];

if (!mines[next_id].visited && get_dist_sq(x, y, i, j) <= r * r) {

dfs(next_id);

}

}

}

}

}

int main() {

ios::sync_with_stdio(false);

cin.tie(nullptr);

int n, m;

cin >> n >> m;

for (int i = 0; i < n; i++) {

int x, y, r;

cin >> x >> y >> r;

LL key = (LL)x * OFFSET + y;

if (pos_to_id.count(key)) {

int id = pos_to_id[key];

mines[id].r = max(mines[id].r, r); // 同一位置取半径最大的

mines[id].num++;

} else {

pos_to_id[key] = mines.size();

mines.push_back({x, y, r, 1, false});

}

}

for (int i = 0; i < m; i++) {

int x, y, r;

cin >> x >> y >> r;

// 火箭引爆炸雷

for (LL i_x = x - r; i_x <= x + r; i_x++) {

for (LL j_y = y - r; j_y <= y + r; j_y++) {

LL key = i_x * OFFSET + j_y;

if (pos_to_id.count(key)) {

int id = pos_to_id[key];

if (!mines[id].visited && get_dist_sq(x, y, i_x, j_y) <= (LL)r * r) {

dfs(id);

}

}

}

}

}

cout << ans << endl;

return 0;

}

相关推荐
Crazyong2 小时前
FreeRTOS-互斥量-2
算法
啊我不会诶2 小时前
2025 北京市大学生程序设计竞赛暨“小米杯”全国邀请赛
c++·学习·算法
mit6.8242 小时前
懒更新|单点查询
算法
Yupureki2 小时前
《C++实战项目-高并发内存池》8. 最终性能优化与测试
c语言·开发语言·数据结构·c++·算法·性能优化
DeepModel2 小时前
【概率分布】均匀分布的原理、推导与Python实现
python·算法·概率论
一叶落4382 小时前
LeetCode 74 | 搜索二维矩阵(C语言版题解)
c语言·数据结构·c++·算法·leetcode·矩阵·动态规划
罗湖老棍子2 小时前
星际信号塔 —— 单调栈经典应用详解
数据结构·算法·单调栈
iAkuya2 小时前
(leetcode)力扣100 96.只出现一次的数字(位运算)
算法·leetcode·职场和发展
Tisfy2 小时前
LeetCode 1622.奇妙序列:懒更新
数学·算法·leetcode·题解·设计