poj3669 Meteor Shower
题目:http://poj.org/problem?id=3669
在一个矩形区域,区域大小为0<=X<=300, 0<=Y<=300,有所谓流星坠落,给出M个流星坠落的时间和地点,问最少需要多少时间步能走到一个安全的格子上,从而不会遭受流星的撞击。每个流星坠落在某一格点后,其相邻四个格点会遭到破坏。
样例输入:
4
0 0 2
2 1 2
1 1 2
0 3 5
样例输出:
5
思路:
- 假如起点(0,0)不会遭受到流星的攻击(也不会因为周围格子被攻击而延伸至它),那么也就不用移动就能保证安全。如果起点会受到攻击,不论是何时,我们都需要离开起点,那么哪里才是安全的终点呢?只要这个点上不会在有流星的攻击,这个点就是安全的终点,我们bfs出第一个安全的终点就是最少的时间步。
- 根据输入我们可以标记每个受流星攻击的网格的时间,注意如果一个网格有多次被流星摧毁的时间,那么取最小的作为这个网格的摧毁时间,同时需要将这个网格上下左右四个网格也“以最近摧毁”原则更新被摧毁的时间。
代码:
#include <iostream>
#include <memory.h>
#include <queue>
#include <utility>
using namespace std;
typedef pair<int, int> PII;
const int N = 302;
int dx[] = {0, 1, 0, -1};
int dy[] = {-1, 0, 1, 0};
int time[N][N];
int g[N][N];
int bfs(){
time[0][0] = 0;
if(time[0][0] >= g[0][0]) return -1; //如果起点在时间0就被炸了, 那么直接失败...
if(g[0][0] == 0x3f3f3f3f) return 0; //如果起点不会被炸, 那么就不用走了...
queue<PII> q;
q.push(make_pair(0, 0));
int time_step = 0;
while(!q.empty()){
PII p = q.front(); q.pop();
for(int i = 0; i < 4; ++i){
int x = p.first + dx[i], y = p.second + dy[i];
if(x >= 0 && x <= N && y >= 0 && y <= N){
if(time[x][y] == -1){ //这个网格没有被访问过(不能走回头路)
time[x][y] = time[p.first][p.second] + 1;
if(g[x][y] == 0x3f3f3f3f){ //到达"终点"
return time[x][y];
}
if(time[x][y] < g[x][y] && g[x][y] != 0x3f3f3f3f) q.push(make_pair(x, y)); //如果这个网格要被炸但目前是安全的, 那么放入队列中
}
}
}
}
return -1;
}
int main(){
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int M; scanf("%d", &M);
memset(g, 0x3f, sizeof(g)); //g记录网格被摧毁的时间T, 初始为正无穷(0x3f3f3f3f), 表示不会被摧毁
memset(time, -1, sizeof(time)); //time记录到某一网格的时间步数,-1表示未访问
while(M--){
int x, y, t;
scanf("%d%d%d", &x, &y, &t);
if(g[x][y] > t) g[x][y] = t;
for(int i = 0; i < 4; ++i){ //按"最近时间摧毁"原则记录每个网格被摧毁的时间
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx <= N && ny >= 0 && ny <= N){
if(g[nx][ny] > t)
g[nx][ny] = t;
}
}
}
printf("%d", bfs());
}