【luogu P7473】重力球

重力球

题目链接:luogu P7473

题目大意

有一个图中有一些障碍物,边界也是障碍物。
然后又每个询问给出两个小球的位置,你可以改变重力变成左右前后,问你最少要改变多少次重力才能使得两个小球滚到一起。
如果不能滚到一起输出 -1,多组询问。

思路

因为是根据重力,那在一次滚动之后,这个点停留在那里一定是因为有障碍物挡住了它,那也就说,有效的点就是旁边四个方向有障碍物的点。

那可以根据数据算出点最多不超过 2000 个。
那我们可以先看看一个询问怎么弄,那因为我们把点压缩到这么小,所以我们可以直接暴搜。
(不过要先自己模拟一步走到关键点)

当然,我们可以先通过一个简单的 DP 算出一个点往四个方向走可以到哪里。
就如果那个方向走一步是障碍物,那就只能走到自己,否则就在走到的地方往那个方向继续走。
然后用合适的顺序可以直接不用继续走,因为当时已经算出了答案。

但是它是多组询问,而且询问还挺多,105 个。
基本上明摆着要直接预处理出所有答案。

你考虑反过来想,你枚举最终会和的地方,然后看可以从那两个位置走到。
那你把每个会和的地方放进队列里面进行 bfs,每次就选一个方向,然后从可以走过来的地方各选一个转移。
那就是反向建边。
然后你就可以这样搜出两个关键点会和所要的最少步数。

那你就根据上面的一样,先走一步使两个都到关键点,然后就可以把四个方向得出的距离取最小值。
那你会想,如果它一步都不走呢?
那就是一开始就在同一个位置,特判一下就好。

代码

#include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct node { int to, nxt; }e[100001]; struct state { int x, y; }; queue <state> q; int le[2001][4], KK, tmp; int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}; int n, m, Q, x, y, tot, px[2001], py[2001]; int pl[251][251], go[251][251][4][2]; int dis[2001][2001], ans; int x1, y1, x2, y2; bool ck(int x, int y) { if (pl[x][y] == -1) return 0; if (x < 1 || x > n) return 0; if (y < 1 || y > n) return 0; return 1; } void add(int x, int y, int way) {//因为你要每次走的方向一样,所以 head 要开四个分开记录四个方向的 swap(x, y);//记得是反向建边 e[++KK] = (node){y, le[x][way]}; le[x][way] = KK; } void bfs() {//bfs 暴搜 for (int i = 1; i <= tot; i++) q.push((state){i, i}), dis[i][i] = 1;//一开始要走一步才能走到关键点 while (!q.empty()) { state now = q.front(); q.pop(); x = now.x; y = now.y; for (int k = 0; k < 4; k++)//每次走的方向要一样 for (int i = le[x][k]; i; i = e[i].nxt) for (int j = le[y][k]; j; j = e[j].nxt) if (dis[e[i].to][e[j].to] == tmp) q.push((state){e[i].to, e[j].to}), dis[e[i].to][e[j].to] = dis[x][y] + 1; } } int main() { scanf("%d %d %d", &n, &m, &Q); for (int i = 1; i <= m; i++) { scanf("%d %d", &x, &y); pl[x][y] = -1; } for (int i = 1; i <= n; i++) pl[0][i] = pl[n + 1][i] = pl[i][0] = pl[i][n + 1] = -1; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (pl[i][j] != -1) { for (int k = 0; k < 4; k++) if (pl[i + dx[k]][j + dy[k]] == -1) { pl[i][j] = ++tot;//给有需要的位置编号 break; } } for (int i = 1; i <= n; i++)//DP 出四个方向可以走到哪里 for (int j = 1; j <= n; j++) if (ck(i, j)) { if (ck(i + dx[2], j + dy[2])) go[i][j][2][0] = go[i + dx[2]][j + dy[2]][2][0], go[i][j][2][1] = go[i + dx[2]][j + dy[2]][2][1]; else go[i][j][2][0] = i, go[i][j][2][1] = j; if (ck(i + dx[3], j + dy[3])) go[i][j][3][0] = go[i + dx[3]][j + dy[3]][3][0], go[i][j][3][1] = go[i + dx[3]][j + dy[3]][3][1]; else go[i][j][3][0] = i, go[i][j][3][1] = j; } for (int i = n; i >= 1; i--) for (int j = n; j >= 1; j--) if (ck(i, j)) { if (ck(i + dx[0], j + dy[0])) go[i][j][0][0] = go[i + dx[0]][j + dy[0]][0][0], go[i][j][0][1] = go[i + dx[0]][j + dy[0]][0][1]; else go[i][j][0][0] = i, go[i][j][0][1] = j; if (ck(i + dx[1], j + dy[1])) go[i][j][1][0] = go[i + dx[1]][j + dy[1]][1][0], go[i][j][1][1] = go[i + dx[1]][j + dy[1]][1][1]; else go[i][j][1][0] = i, go[i][j][1][1] = j; } for (int i = 1; i <= n; i++)//建边 for (int j = 1; j <= n; j++) if (pl[i][j] > 0) for (int k = 0; k < 4; k++) add(pl[i][j], pl[go[i][j][k][0]][go[i][j][k][1]], k); memset(dis, 0x7f, sizeof(dis)); tmp = dis[0][0]; bfs(); while (Q--) { scanf("%d %d %d %d", &x1, &y1, &x2, &y2); if (x1 == x2 && y1 == y2) {//一开始就在用一个位置 printf("0\n"); continue; } ans = tmp; for (int i = 0; i < 4; i++)//一开始选一个方向走到关键点 ans = min(ans, dis[pl[go[x1][y1][i][0]][go[x1][y1][i][1]]][pl[go[x2][y2][i][0]][go[x2][y2][i][1]]]); if (ans == tmp) { printf("-1\n"); } else printf("%d\n", ans); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P7473.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(119)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示