POJ 3669 Meteor Shower 流星雨 解题思路心得 BFS广搜 C/C++AC代码(另有TLE不知其因)

原题

http://poj.org/problem?id=3669

题意

贝西(Bessie)听说即将发生一场异常的流星雨;有报道称这些流星将坠入大地并摧毁其所击中的任何东西,为安全着急,她发誓要找到一个安全的地方(一个从未被流星摧毁的路)。
她目前正在坐标平面上的原点放牧,并希望移至新的更安全的位置,同时避免沿途被流星摧毁。

报告说,在时间Ti(0≤Ti≤1,000)时,M个流星(1≤M≤50,000)将撞击,流星i将撞击点(Xi,Yi)(0≤Xi≤300; 0≤Yi≤300)。
每个流星都会破坏其撞击的点以及四个直线相邻的晶格点。

贝西在时间0离开原点,并且可以以每秒一个距离单位的速度在第一象限中平行于轴移动,到达尚未被流星破坏的任何(通常为4个)相邻直线点。
在大于或等于销毁时间的任何时间位于一个点上)。

确定Bessie到达安全地点所需的最短时间。

输入值

*第1行:单个整数:M

*第2…M + 1行:第i + 1行包含三个以空格分隔的整数:Xi,Yi和Ti

输出量

*第1行:贝西到达安全地点所需的最短时间;如果不可能,则为-1。

Sample Input

4
0 0 2
2 1 2
1 1 2
0 3 5

Sample Output

5

思路

最短存活时间,用BFS 起点为(0,0),终点为安全地带。
m[][]表示各个地方被流星雨轰炸的时间,初始化为INF,即安全,输入的同时对其进行初始化,特别注意,因爆炸波及5个单位,爆炸区域可能存在重叠,并且刚好爆炸和爆炸后都无发进入,所以t[]总是取最小的爆炸时间点。

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=1<<30;
int M,maze[388][388];
int d[5][2]={{0,0},{0,1},{1,0},{0,-1},{-1,0}};
typedef struct {
	int x,y,t;
}node;
int in(int x,int y){
	return x>=0&&y>=0;
}
int bfs()
{
	queue <node> que;
	if(maze[0][0]>0)	que.push(node{0,0,0});
	while(!que.empty()){
		node cur=que.front();
		que.pop();
		int &x=cur.x,&y=cur.y,&t=cur.t;
		if(maze[x][y]==INF)	return t;
		for(int i=1;i<5;i++){
			int nx=x+d[i][0],ny=y+d[i][1];
			//不越界  且  到下一格的时间比爆炸时间早 
			if(in(nx,ny)&&t+1<maze[nx][ny]){
				//防止回退 
				if(maze[nx][ny]!=INF)	maze[nx][ny]=t+1; 
				que.push(node{nx,ny,t+1});
			}
		}	
	}
	return -1;
}
void solve()
{
	int ans=bfs();
	printf("%d\n",ans);
}
int main()
{
	void solve();
	scanf("%d",&M);
	fill(maze[0],maze[0]+388*388,INF);
	while(M--){
		int x,y,t;
		scanf("%d %d %d",&x,&y,&t);
		for(int i=0;i<5;i++){
			int nx=x+d[i][0],ny=y+d[i][1];
			if(in(nx,ny))	maze[nx][ny]=min(t,maze[nx][ny]);
		}
	}
	solve();
	return 0;
}

参考一位大佬的代码:

https://blog.csdn.net/a1097304791/article/details/101617157

TLE代码 大佬看出哪有问题请不吝赐教

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<queue> 
#define INF 0x3f3f3f3f
#define ff first
#define ss second
using namespace std;
typedef pair<int,int> P;
int M,ans,m[400][400],d[400][400];//m[]用来存放各个地点被炸时间  d[]用来存放从原点到对应位置所需时间 
int x[50100],y[50100],t[50100];//记录每一颗流星爆炸坐标x,y及时间t 参考他人代码得知也可用结构体 
int move[5][2]={0,1,1,0,0,-1,-1,0,0,0};//右下左上[本身] 这里顺序 下面有一处需要5个 有一处需要移动只需4个 
int main()
{
	void solve();
	int in(int x,int y);
	memset(m,0x3f,sizeof(m));
	scanf("%d",&M);
	for(int i=0;i<M;i++)
	{
	 	scanf("%d %d %d",&x[i],&y[i],&t[i]);
	 	for(int j=0;j<5;j++)//需要5个 写入m[][] 
	 	{
	 		int &dx=move[j][0];
			int &dy=move[j][1];
			if(in(x[i]+dx,y[i]+dy)&&m[x[i]+dx][y[i]+dy]>t[i])//★★注意:输入数据时,要考虑该点及附近的四个点对应的时间是否小于新输入的时间,若小于则无需改动,反之,改变这五个点对应的时间
	 			m[x[i]+dx][y[i]+dy]=t[i];//m[][]中  INF==安全  !INF==不安全 ★小心越界问题★
		}
	}
	solve();
	return 0;
}
void solve()
{
	int bfs();
	ans=bfs();
	printf("%d\n",ans); 
}
int bfs()
{
	int in(int x,int y);
	int boom(int x,int y,int tt);
	if(m[0][0]==0)//★★如果m[0][0]在时间0时就爆炸 那么她永远也到达不了【存活】的【真实】 
		return -1;
	queue <P> que;
	que.push(P(0,0));
	d[0][0]=0;
	while(que.size())
	{
		P p=que.front();
		que.pop();
		if(m[p.ff][p.ss]==INF)//到达安全地带
			return d[p.ff][p.ss];
		for(int i=0;i<4;i++)//这里只需4个 (0,0)不算移动 
		{
			int &dx=move[i][0];
			int &dy=move[i][1];
			int nx=p.ff+dx;
			int ny=p.ss+dy;
			if(in(nx,ny)&&!boom(nx,ny,d[p.ff][p.ss]+1)) //能走的条件:不越界  且 下一秒到达的位置 不会被炸	
			{
				que.push(P(nx,ny));
				d[nx][ny]=d[p.ff][p.ss]+1;
			}
		}
	}
	return -1;//当queue中无元素时 还会被炸 则逃跑失败
}
int boom(int xx,int yy,int tt)//点(xx,yy)在tt时是否会被炸 默认(xx,yy)不越界
{
	int in(int x,int y);
	for(int i=0;i<M;i++)//判断 此不安全地 此时 是否会被炸  (注:此地默认不是安全地带 bfs前段:如安全就结束了)
	{
		for(int j=0;j<5;j++)//我他喵的i++看了1小时没看出来 艹 
		{
			int &dx=move[j][0];//定义 引用 
			int &dy=move[j][1];//爆炸中心是自己输入的 不可能越界
			if(in(x[i]+dx,y[i]+dy))//★小心越界问题★ 
			{
				if((xx==x[i]+dx)&&(yy==y[i]+dy)&&(tt>=m[xx][yy]))//注意 
					return 1;
			}
		}	
	}
	return 0;
}
int in(int x,int y)
{
	if(x>=0&&x<400&&y>=0&&y<400)
		return 1;
	return 0;
}

总结

①输入输出尽量使用scanf/printf,不用cin/cout,因前者运行效率高,如用后者易TLE
②移动向量可以加(0,0)表示本身,本题中m[][]初始化时需要
③int &dx=move[j][0] dx是引用,别名
④本题当初始化m[][]时,特别要注意爆炸区域会重叠,所以每一块的值应为最早爆炸时间
⑤使用方向移动向量时特别要注意数组越界问题
⑥本题还需注意当(0,0)在t=0时被轰炸,她远也到达不了【存活】的【真实】
⑦i,j看清楚别眼花
⑧TLE可能原因:总结①;没有防止回退(可以用vis[]标记)那位大佬代码里注释的最佳化剪枝应该也是起这个作用;?代码开了太多数组(代码不够简洁精妙)?待定

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_小鹰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值