CF1301F Super Jaber
最终答案的方案可以分为2种:1、不使用传送 2、从起点s到最近(步数最少)的颜色为c的格子x,传送到离终点t最近的颜色也为c的格子y,再走到t(从s到x,y到t的过程中均可使用传送)
询问次数多,如果预处理出f[k][i][j]表示从格子(i, j)到一个颜色为k的格子的最少步数,询问时只要枚举中间格子的颜色取最小值即可得出答案
关键在于预处理的方法喽
把每种颜色分开处理(对f数组的每个k处理一遍),如果在上述过程中,从s到x,y到t的过程中不能使用传送,普通的多源bfs即可解决问题
处理中间过程的传送方法也简单:对于每一种颜色color,多源bfs的过程中第一次走到颜色为color的格子上时,将所有颜色为color且未被访问的格子都放入队列
每次多源bfs中每个格子只访问一遍,复杂度O(n),预处理总复杂度O(nk),询问复杂度O(mk),总复杂度O((n+m)k)
#include <bits/stdc++.h>
using namespace std;
inline void read (int &x) {
char ch = getchar(); x = 0;
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1024, K = 50;
int n, m, k, a[N][N], f[K][N][N];
int sx[4] = {0, 1, 0, -1}, sy[4] = {1, 0, -1, 0};
vector <pair<int, int> > v[K];
queue <pair<int, int> > que[K]; int used[K], vis[N][N];
inline void work (int p) {
for (int i = 1; i <= k; ++i) used[i] = 0;
while (que[p].size()) {
int x = que[p].front().first, y = que[p].front().second;
for (int i = 0; i < 4; ++i) {
int tx = x + sx[i], ty = y + sy[i];;
if (tx < 1 || ty < 1 || tx > n || ty > m || f[p][tx][ty]) continue;
f[p][tx][ty] = f[p][x][y] + 1; que[p].push (make_pair (tx, ty));
} if (!used[a[x][y]]) {
used[a[x][y]] = 1;
for (int i = 0; i < v[a[x][y]].size(); ++i) {
int tx = v[a[x][y]][i].first, ty = v[a[x][y]][i].second;
if (!f[p][tx][ty]) f[p][tx][ty] = f[p][x][y] + 1, que[p].push (make_pair (tx, ty));
}
} que[p].pop();
}
}
#define Min(x, y) (x > y ? y : x)
signed main() {
read (n), read (m), read (k);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
read (a[i][j]), f[a[i][j]][i][j] = 1;
v[a[i][j]].push_back (make_pair (i, j)), que[a[i][j]].push (make_pair (i, j));
}
for (int i = 1; i <= 40; ++i) work (i);
int T, xi, yi, xj, yj; read (T);
while (T--) {
read (xi), read (yi), read (xj), read (yj);
int res = abs (xi - xj) + abs (yi - yj);
for (int i = 1; i <= k; ++i) res = Min (res, f[i][xi][yi] + f[i][xj][yj] - 1);
printf ("%d\n", res);
} return 0;
}