扫雷


题目描述:

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

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

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

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

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

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

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

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

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

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

解题思路:

  1. 暴力扫描所有可能爆炸的点:

    无疑啊,这种暴力的复杂度在O(n\(^2\))的,0<=n<=5e4,肯定会TLE滴

考虑到半径0<=r<=10,所以我们考虑能不能只扫描炸弹半径内可能会被引爆的点,那复杂度就会被降下来

  1. 哈希+bfs:

    我们将所有的炸弹用哈希映射到到一个唯一的哈希值然后在bfs的过程中,扫描当前炸弹的半径中是否存在炸弹(哈希查找),这样的总复杂度就在O(2r\(^2\)n+2r\(^2\)m)

    其中:bfs(O(n)),哈希(O(1))

哈希:map<key,val>

考虑哈希的细节:

  1. 二维坐标x,y唯一映射的哈希值:

    由于x,y的范围为[0,1e9],所以我们可以将x,y唯一的映射到一个哈希值(x*mod+y),mod = 1e9+1

    这就是将x,y映射为一个1e9+1进制的数,假设这个哈希值为h,那x = h/mod,y = h%mod
  2. 哈希表的长度:已知题目n小于等于5e4,哈希表的长度至少是2n,尽可能的开大空间,避免冲突的发生,同时哈希表的长度应该为一个质数,所以取1e6+7
  3. 哈希表的key:获取哈希值之后需要将其存入哈希表的一个位置,这个位置就是该哈希值的key。对长度取模即可,若冲突则后移。

代码实现:

# include<bits/stdc++.h>
using namespace std;
# define int long long
const int N = 5e4+10,M = 1e6+7,mod = 1e9+7;
int h[M];//哈希表
int id[M];//对应哈希key的炸弹下标
bool vis[N];//是否被引爆过
struct node{
int x,y,r;
}a[N];
int n,m; /*
哈希:map<key,value>
*/
int get_hs(int x,int y)//获取哈希值
{
return (int)x*mod+y;
}
int find(int x,int y)//获取哈希的key
{
int hs = get_hs(x,y);
int key = (hs%M+M)%M; while(h[key] != -1&&h[key] != hs)//如果冲突就后移
{
key++;
if(key == M) key = 0;
}
return key;
} bool check(int x,int y,int r,int a,int b)//是否会被引爆
{
int d = (x-a)*(x-a)+(y-b)*(y-b);
return d<=r*r;
} void bfs(int pos){
queue<int> q;
q.push(pos);
vis[pos] = 1;
while(q.size()){
int t = q.front();
q.pop();
int x = a[t].x,y = a[t].y,r = a[t].r;
/*
扫描整个半径内是否存在会被引爆的炸弹
*/
for(int xx = max(x-r,0ll);xx<=min(x+r,(int)1e9);++xx){
for(int yy = max(y-r,0ll);yy<=min(y+r,(int)1e9);++yy){
int key = find(xx,yy);
if(id[key]&&!vis[id[key]]&&check(x,y,r,xx,yy)){
int pos = id[key];
vis[pos] = 1;
q.push(pos);
}
}
}
}
} signed main(){
cin>>n>>m;
memset(h,-1,sizeof h);
int x,y,r;
for(int i = 1;i <= n;++i){
cin>>x>>y>>r;
a[i] ={x,y,r};
int key = find(x,y);//获取key
if(h[key] == -1) h[key] = get_hs(x,y);//存储哈希值 if(!id[key]||a[id[key]].r<r)//如果该点有炸弹,或者有一个半径更大的炸弹
{
id[key] = i;
}
} for(int i = 1;i <= m;++i){
cin>>x>>y>>r; // 扫描整个半径内是否存在会被引爆的炸弹 for(int xx = max(x-r,0ll);xx<=min(x+r,(int)1e9);++xx){
for(int yy = max(y-r,0ll);yy<=min(y+r,(int)1e9);++yy){
int key = find(xx,yy);
//是否有炸弹,是否被引爆过,是否能被引爆
if(id[key]&&!vis[id[key]]&&check(x,y,r,xx,yy)){
bfs(id[key]);
}
}
}
}
int ans = 0;
//扫描答案
for(int i = 1;i <= n;++i){
int key = find(a[i].x,a[i].y);
int pos = id[key];
if(pos&&vis[pos]) ans++;
}
cout<<ans<<endl; return 0;
}

扫雷(哈希+bfs)的更多相关文章

  1. 哈希+Bfs【P2730】 魔板 Magic Squares

    没看过题的童鞋请去看一下题-->P2730 魔板 Magic Squares 不了解康托展开的请来这里-->我这里 至于这题为什么可以用康托展开?(瞎说时间到. 因为只有8个数字,且只有1 ...

  2. 山东省第四届ACM省赛

    排名:http://acm.sdut.edu.cn/sd2012/2013.htm 解题报告:http://www.tuicool.com/articles/FnEZJb A.Rescue The P ...

  3. poj 2432 Around the world bfs+哈希

    由于每个点的状态包含走过来的距离,所以要存二维的状态,但是状态总量太多,所以可以用哈希来搞. 那么就是bfs最短路,哈希记录状态了. #include <iostream> #includ ...

  4. Eight(bfs+全排列的哈希函数)

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22207   Accepted: 9846   Special Judge ...

  5. UVA 10651 Pebble Solitaire(bfs + 哈希判重(记忆化搜索?))

    Problem A Pebble Solitaire Input: standard input Output: standard output Time Limit: 1 second Pebble ...

  6. hdu1067-Gap(bfs+哈希)

    Let's play a card game called Gap. You have 28 cards labeled with two-digit numbers. The first digit ...

  7. Poj2946-The Warehouse(bfs+哈希)

    题目我就不粘贴了... 题意:给出地图,最大8*8,出口用'E'表示,空地用'.'表示,数字表示此处有多少个箱子,主人公的起点应该是在有箱子的地方,他可以朝四个方向移动,但是只有两种方式 一种是他移动 ...

  8. 【算法】BFS+哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...

  9. POJ-3131-Cubic Eight-Puzzle(双向BFS+哈希)

    Description Let's play a puzzle using eight cubes placed on a 3 × 3 board leaving one empty square. ...

随机推荐

  1. k8s 网络持久化存储之StorageClass(如何一步步实现动态持久化存储)

    StorageClass的作用: 创建pv时,先要创建各种固定大小的PV,而这些PV都是手动创建的,当业务量上来时,需要创建很多的PV,过程非常麻烦. 而且开发人员在申请PVC资源时,还不一定有匹配条 ...

  2. 再谈NULL和nullptr(C++11)区别

    在谈NULL和nullptr区别之前,我们先看段代码: #include "stdafx.h" #include <iostream> using namespace ...

  3. 自定义View4-塔防小游戏第一篇:一个防御塔+多个野怪(简易版)*

    塔防小游戏 第一篇:一个防御塔+多个野怪(简易版)    1.canvas画防御塔,妖怪大道,妖怪行走路线    2.防御塔攻击范围是按照妖怪与防御塔中心距离计算的,大于防御塔半径则不攻击,小于则攻击 ...

  4. KingbaseES 数据库大小写敏感特性

    针对不同版本.是否启用大小写敏感,特征汇总如下:

  5. 微信小程序-全局配置、组件、页面跳转、用户信息等

    全局配置 三个页面 app.json pages字段 "pages":[ "pages/index/index", # 首页 "pages/home/ ...

  6. MQ的消息丢失/重复/积压的问题解决

    在我们实际的开发过程中,我们肯定会用到MQ中间件,常见的MQ中间件有kafka,RabbitMQ,RocketMQ.在使用的过程中,我们必须要考虑这样一个问题,在使用MQ的时候,我们怎么确保消息100 ...

  7. 【疑难杂症】if __name__ == '__main__'的理解

    今天进行小批量梯度下降时,代码给我报错,具体代码如下 import torch import numpy as np from torch.utils.data import Dataset from ...

  8. 第六章:Django 综合篇 - 6:会话session

    因为因特网HTTP协议的特性,每一次来自于用户浏览器的请求(request)都是无状态的.独立的.通俗地说,就是无法保存用户状态,后台服务器根本就不知道当前请求和以前及以后请求是否来自同一用户.对于静 ...

  9. Linux实例常用内核网络参数介绍与常见问题处理---重要

    文章转载自:https://help.aliyun.com/knowledge_detail/41334.html 本文主要介绍如下几点内容,您可以根据实际需要选择. 查看和修改Linux实例内核参数 ...

  10. 实用脚本:检查高 CPU / 内存消耗进程

    1 检查高 CPU 消耗进程在 Linux 上运行了多长时间的 Bash 脚本 该脚本将帮助你确定高 CPU 消耗进程在 Linux 上运行了多长时间. # vi /opt/scripts/long- ...