原题
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[]标记)那位大佬代码里注释的最佳化剪枝应该也是起这个作用;?代码开了太多数组(代码不够简洁精妙)?待定