P7473 重力球
Solution
考虑 Brute Force:对于每一次询问,通过 BFS 处理出最近的交汇点,输出答案。
很显然,会 TLE \colorbox{navy}{\color{white}{TLE}} TLE。
故,考虑 优化:
观察发现障碍物数量非常少,故设状态 ( x , y , d ) \mathrm{(x,y,d)} (x,y,d) 表示第 1 1 1 个小球被第 x x x 个障碍物卡住,第 2 2 2 个小球被第 y y y 个小球卡住,在障碍物的 d d d 方向(如图)。此时,发现总共的状态数量不多。
这样我们就可以用着 3 3 3 个数来确定出每个小球的位置。
之后,考虑每一次走一定是走到一个 障碍物四周 的点,所以可以提前预处理,记作 T o i , j , k \mathrm{To_{i,j,k}} Toi,j,k 表示 ( i , j ) (i,j) (i,j) 向 k k k 方向会走到的点。
对于任意两个起点,需要向四周可到达的点连边:
假设当前是 x 1 , y 1 , d \mathrm{x_1,y_1,d} x1,y1,d,方向是 2 2 2,那么运动之后, d = d ⊕ 2 \mathrm{d=d\oplus 2} d=d⊕2(即原来在上面,变成下面;原来在左边,变成右边…… 通过上图观察发现异或 2 2 2 可以解决);点会变为 T o x 1 , y 1 , 2 \mathrm{To_{x_1,y_1,2}} Tox1,y1,2
依此建边即可。
预处理操作结束之后,对于每一个询问:
- 直接 BFS 算出终点为哪个点最近(易 T L E \mathrm{TLE} TLE)
- 正难则反,考虑对于所有可能终点中到这两个点的最短距离,这其实就是一个
多源BFS
!注意:使用该方法需建反边!
建议采取第 2 2 2 种操作,当然这样并不需要每一次都进行 BFS,只需提前做 1 1 1 次即可。
由于,每一次询问的时候,不一定是在一个 障碍物四周,所以需要上、下、左、右四个方向,跑到障碍物四周,然后取最小值即可。
Code
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>using namespace std;typedef pair<int, int> PII;
typedef long long LL;const int SIZE = 255;int N, M, Q;
vector<PII> Obstackle;
int Is[SIZE][SIZE], Id[SIZE][SIZE], cnt;
int dx[4] = {0, -1, 0, 1}, dy[4] = {-1, 0, 1, 0};
int To[SIZE][SIZE][4], Dist[SIZE * 5][SIZE * 5][4];
struct P
{int x, y, d;
};
std::vector<P> G[SIZE * 5][SIZE * 5][4];
int Vis[SIZE * 5][SIZE * 5][4];bool check(int x, int y)
{return x >= 1 && y >= 1 && x <= N && y <= N && !Is[x][y];
}void Init()
{for (int i = 1; i <= N; i ++)for (int j = 1; j <= N; j ++)for (int k = 0; k < 4; k ++){int x = i, y = j;while (!Is[x][y])x += dx[k], y += dy[k];To[i][j][k] = Id[x][y];}for (auto i : Obstackle)for (auto j : Obstackle)for (int sd = 0; sd < 4; sd ++)for (int k = 0; k < 4; k ++){int x1 = i.first + dx[k], y1 = i.second + dy[k], x2 = j.first + dx[k], y2 = j.second + dy[k];if (check(x1, y1) && check(x2, y2)){int To1 = To[x1][y1][sd], To2 = To[x2][y2][sd];G[To1][To2][sd ^ 2].push_back({Id[i.first][i.second], Id[j.first][j.second], k});}}
}void BFS()
{memset(Dist, 0x3f, sizeof Dist);queue<P> Q;for (auto c : Obstackle)for (int i = 0; i < 4; i ++){int id = Id[c.first][c.second];int x = c.first + dx[i], y = c.second + dy[i];if (check(x, y))Dist[id][id][i] = 0, Q.push({id, id, i});}while (Q.size()){auto Tmp = Q.front();Q.pop();if (Vis[Tmp.x][Tmp.y][Tmp.d]) continue;Vis[Tmp.x][Tmp.y][Tmp.d] = 1;for (auto c : G[Tmp.x][Tmp.y][Tmp.d])if (Dist[c.x][c.y][c.d] > Dist[Tmp.x][Tmp.y][Tmp.d] + 1){Q.push(c);Dist[c.x][c.y][c.d] = min(Dist[c.x][c.y][c.d], Dist[Tmp.x][Tmp.y][Tmp.d] + 1);}}
}signed main()
{cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);cin >> N >> M >> Q;for (int i = 1; i <= M; i ++){int x, y;cin >> x >> y;Id[x][y] = ++ cnt;Is[x][y] = 1;Obstackle.push_back({x, y});}for (int i = 0; i <= N + 1; i ++)for (int j = 0; j <= N + 1; j ++)if (!i || !j || i > N || j > N){Id[i][j] = ++ cnt;Is[i][j] = 1;Obstackle.push_back({i, j});}Init();BFS();while (Q --){int x1, y1, x2, y2;cin >> x1 >> y1 >> x2 >> y2;int Result = 2e9;for (int i = 0; i < 4; i ++)Result = min(Result, Dist[To[x1][y1][i]][To[x2][y2][i]][i ^ 2] + 1);if (x1 == x2 && y1 == y2) Result = 0;if (Result >= 1e9) Result = -1;cout << Result << endl;}return 0;
}