【洛谷】P8785 [蓝桥杯 2022 省 B] 扫雷 的题解

文章介绍了如何使用深度优先搜索(DFS)解决蓝桥杯比赛中关于扫雷的编程问题。通过排序和二分查找优化搜索范围,避免了时间复杂度达到O(mn),从而提高了算法效率。代码示例展示了如何构建和遍历数据结构来统计可引爆的雷的数量。
摘要由CSDN通过智能技术生成

【洛谷】P8785 [蓝桥杯 2022 省 B] 扫雷 的题解

题目传送门

思路

定义结构体储存 ( x , y , r , f l a g ) (x,y,r,flag) (x,y,r,flag),分别表示坐标,爆炸半径,以及是否已爆炸。依次引爆排雷火箭,搜索到一个雷如果在爆炸范围内继续递归搜索。但时间复杂度为 O ( m n ) O(mn) O(mn),只能拿到前 40 40 40 分。

题目中每一个雷的爆炸范围与他们之间的距离相比实在是太小了,所以深搜过程中对与所有个雷的判断很多余。我们只要在一开始先对每一个雷进行按 x x x 位置进行从小到大排序,这样每次搜索的 x x x 范围是 x − r → x + r x−r→ x+r xrx+r,范围之外的可以直接排除,剩下的一小部分才是要真正要搜索的。在判断搜索范围时,使用二分查找来实现,这样可以达到优化作用。

此题不可以用并查集去做,因为雷的爆炸是由方向的, A A A 雷爆炸可以引爆 B B B 雷,但是 B B B 雷爆炸是有可能无法引爆 B B B 雷的(爆炸半径不同)

正解是深搜,为了避免边被卡成 O ( n 2 ) O(n^2) O(n2),以至于超时。因此我们就需要去除重复的点,记录每一个坐标下点的位置。

然后把每一个火箭作为根节点,去遍历周围 r 2 r^2 r2 的雷,统计一下一共能炸多少雷就可以。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <climits>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <ctime>
#include <string>
#include <cstring>
#define lowbit(x) x & (-x)
#define endl "\n"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace fastIO {
	inline int read() {
		register int x = 0, f = 1;
		register char c = getchar();
		while (c < '0' || c > '9') {
			if(c == '-') f = -1;
			c = getchar();
		}
		while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
		return x * f;
	}
	inline void write(int x) {
		if(x < 0) putchar('-'), x = -x;
		if(x > 9) write(x / 10);
		putchar(x % 10 + '0');
		return;
	}
}
using namespace fastIO;
struct Node {
    int x, y, r, l;
}a[200005];
unordered_map <ll, int> idx;
bool st[200005];
int n, m, cnt, ans;
ll cal(int x, int y) {
    return (ll)x * (1000000000 + 1) + y;
}
bool solve(int x, int y, int r) {
    if(x * x + y * y <= r * r) return true;
    else return false;
}
void dfs(int i) {
    st[i] = true;
    ans += a[i].l;
    int r = a[i].r;
    for(int x = max(a[i].x - r, 0); x <= min(a[i].x + r, 1000000000); x ++) {
        for(int y = max(a[i].y - r, 0); y <= min(a[i].y + r, 1000000000); y ++) {
            if(solve(x - a[i].x, y - a[i].y, r)) {
                ll xy = cal(x, y);
                if(idx.count(xy)) {
                    int u = idx[xy];
                    if(!st[u]) {
                        dfs(u);
                    }
                }
            }
        }
    }   
}
int main() {
	//freopen(".in","r",stdin);
    //freopen(".out","w",stdout);	
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ ) {
        int x, y, r;
        cin >> x >> y >> r;
        ll xy = cal(x, y);
        if(idx.find(xy) == idx.end()) {
            idx[xy] = ++cnt; 
            a[cnt] = {x, y, r, 1};
        }
        else {
            int u = idx[xy];
            a[u].r = max(a[u].r, r);
            ++ a[u].l;
        }
    }
    for(int i = 1; i <= m; i ++) {
        int x, y, r;
        cin >> x >> y >> r;
        a[cnt + 1] = {x, y, r};
        dfs(cnt + 1);
    }
    cout << ans;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值