题目描述:
一个人在一个题图中跑路, 每一时刻只能上下左右走一格。 不同的时刻会有流星覆盖在地图单元上, 流星还会将该单元的上下左右四个单元摧毁, 这个人不能走到被摧毁的单元, 问最少需要走多少步才能到安全的地方;
这道题虽然是一道bfs类问题, 但还是让我想到了另外一道题:外卖店优先级
来对比下这两道题:
外卖店优先级: 要求在同一时刻内完成所有外卖店的优先级变化, 最后判断所有外卖店在缓存中的变化;
Meteor Shower: 要求在同一时刻内让人跑一格路, 并且让该时刻坠落的流星覆盖地图;
这两道题相同点是, 题目要求的所有的操作都是在同一时刻内完成的(时刻类问题… 自己造的一个词);
感觉这类问题处理起来还是挺麻烦的, 因为计算机程序语句跑起来是有先后顺序的, 因此怎么用一个有先后顺序的语句来表达同一时刻内要求同时发生的事情是个不容易的事情;
主要的难点有两个:
1. 题目要求的各个操作之间的顺序问题, 既先做什么操作后做什么操作才能使得结果完美符合题目要求不出现矛盾;
2. 如果有多个操作在同一时刻发生, 怎么处理这种情况;
操作顺序问题只能理解题目的意思来解决, 比如这题每一时刻的操作是先让流星坠落再让人移动;
第二个问题可以将所有操作按照时间排序, 对于每一个时刻, 扫描所有与之相同的时刻;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 310;
struct Node
{
int x, y, t;
}node[50010];
int n;
bool g[N][N];
int dx[] = {1, 0, -1, 0, 0}, dy[] = {0, 1, 0, -1, 0};
void turn(int x, int y, bool w[][N])
{
for(int i = 0;i < 5;i ++)
{
int a = x + dx[i], b = y + dy[i];
if(a < 0 || b < 0) continue;
w[a][b] = true;
}
}
bool cmp(Node a, Node b)
{
return a.t < b.t;
}
int bfs(int u, int v)
{
int tail = 0, head = 0;
pair<int, int> q[N * N];
q[tail] = {u, v};
// mp表示被流星覆盖的区域, st表示人走过的区域
// 一开始还想着将这两个数组合并成一个数组, 但后来出现了各种问题, 还是分开来表示方便;
bool mp[N][N], st[N][N];
memset(mp, 0, sizeof mp), memset(st, 0, sizeof st);
st[u][v] = true;
int time = 1, i = 0;
while(tail >= head)
{
int rear = tail;
// 将同一时刻坠落的流星表示出来;
while(i < n && node[i].t == time)
{
turn(node[i].x, node[i].y, mp);
i ++;
}
// 每一次循环只遍历相同层数的节点;
// 在rear和head之间的所有节点是上一个时刻到的点;
// 由这些点遍历出来的点(rear到tail之间的点)才是本时刻到达的点;
while(rear >= head)
{
pair<int, int> t = q[head ++];
if(!g[t.first][t.second]) return time - 1;
for(int i = 0;i < 4;i ++)
{
int a = t.first + dx[i], b = t.second + dy[i];
if(mp[a][b]) continue;
if(st[a][b]) continue;
if(a < 0 || b < 0 ) continue;
st[a][b] = true;
q[++ tail] = {a, b};
}
}
time ++;
}
return -1;
}
int main()
{
cin >> n;
for(int i = 0;i < n;i ++)
{
int x, y, t;
scanf("%d%d%d", &x, &y, &t);
node[i] = {x, y, t};
turn(x, y, g);
}
sort(node, node + n, cmp);
cout << bfs(0, 0) << endl;
return 0;
}