[蓝桥杯 2022 省 B] 扫雷(搜索)

 传送门

第一思路比较容易想到的是使用搜索或者去模拟爆炸的过程

即将当前节点先初始化为排雷火箭,然后遍历所有还未被引爆的地雷,如果某个地雷被当前地雷引爆了,就做个标记,同时将其作为下一节点

下面是使用栈去模拟这个过程的代码,本质上与DFS大差不差

#include<iostream>
#include<stack>
#include<vector>
#include<cmath>
using namespace std;

typedef struct Node{
	int x,y,r;
	bool boom=false;//boom标志该地雷是否被引爆过(题目提到可能存在重复地雷
}Node;
vector<Node>v;//存储地雷 
int n,m,ans=0;

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x,y,r;
		cin>>x>>y>>r;
		Node node;
		node.x=x;node.y=y;node.r=r;
		v.push_back(node);//存地雷
	}
	while(m--){
		int x,y,r,len=v.size();
		cin>>x>>y>>r;
		stack<Node>stk;
		Node node;node.x=x;node.y=y;node.r=r;
		stk.push(node);
		while(!stk.empty()){
			Node now=stk.top();//当前地雷
			stk.pop();
			for(int i=0;i<len;i++){//遍历所有地雷
				Node temp=v[i];
				if(temp.boom)continue;//如果该地雷已经引爆过就跳过
				double dis=sqrt((now.x-temp.x)*(now.x-temp.x)+(now.y-temp.y)*(now.y-temp.y));
				if(dis<=now.r){//距离合适
					v[i].boom=true;//标记					
					stk.push(temp);//作为下一个地雷
					ans++;
				} 
			}
		}
	}
	cout<<ans<<endl;
	return 0;
} 

但是该方法时间复杂度为O(mn)只能拿到40分 

实际上,通过观察数据范围我们不难得知的是,爆炸的范围可能要远远小于炸弹之间的距离,按照上面的代码所写的话,我们遍历了所有的炸弹,实际上可以不用遍历那么多,只选择遍历在当前炸弹附近的即可,因此,这里我们选择以x轴上的距离为标准,将遍历的炸弹范围缩小至只在(x-r,x+r)之间的炸弹(读者可自行画图理解)

据此,我们先对所有的炸弹按照x的大小从小到大进行排序,然后找到最接近x-r距离的炸弹下标和最接近x+r的炸弹下标,然后只对他们之间炸弹的进行遍历即可

#include<bits/stdc++.h>
using namespace std;
long long n,m,ans;
double dis(double x1,double y1,double x2,double y2){
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
struct zhalei{
	long long x,y,r;
	bool flag=0;
}a[50005];
struct pailei{
	long long x,y,r;
}b[50005];//这个结构体是排雷火箭,按顺序引爆时不需要 flag
bool cmp(zhalei a,zhalei b){
	return a.x<b.x;
}
void dfs(int x,int y,int r){
	int Lmid,Rmid,L=1,R=n;
	while(L<=R){
		Lmid=(L+R)/2;
		if(a[Lmid].x<x-r)L=Lmid+1;
		else R=Lmid-1;
	}
	Lmid=L; 
    L=1,R=n;
	while(L<=R){
		Rmid=(L+R)/2;
		if(a[Rmid].x<=x+r)L=Rmid+1;
		else R=Rmid-1;
	}
	for(long long i=Lmid;i<=Rmid;i++){
		if(!a[i].flag&&dis(x,y,a[i].x,a[i].y)<=r){
			a[i].flag=1,ans++;
			dfs(a[i].x,a[i].y,a[i].r);
		}
	}
}
int main(){
	long long i;
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=n;i++)scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r);
	for(i=1;i<=m;i++)scanf("%lld%lld%lld",&b[i].x,&b[i].y,&b[i].r);
	sort(a+1,a+n+1,cmp);
	for(i=1;i<=m&&ans<n;i++)//依次引爆排雷火箭
		dfs(b[i].x,b[i].y,b[i].r);//小优化,已引爆的炸弹数<n时
	printf("%lld",ans);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值