如果暴力 BFS,枚举每个点之后暴力枚举同行同列的所有点,时间复杂度不会低于 \(O(nm(n+m))\),明显过不去,因此必须要以行和列为单位整体考虑。
由题意,路径上所有单元格的 \(A\) 值单调递增,因此类似于拓扑排序的思想,按照 \(A\) 值从小到大枚举单元格。假设枚举到单元格 \((r,c)\)。设 \(f_{r,c}\) 表示走到 \((r,c)\) 的最少步数。考虑对每行每列维护在该行/列中有哪些单元格可以进行有效转移,这个转移形如:
\[f_{r,c}=\min\limits_{(r',c')}\{f_{r',c'}\}+1
\]
其中 \((r',c')\) 满足 \(r'=r\lor c'=c\) 且 \(A_{r,c}-K\le A_{r',c'}<A_{r,c}\)。
发现一行/列中可以进行有效转移的单元格要么 \(f\) 值比较小,要么虽然 \(f\) 比较大,但是 \(A\) 比较小。换言之,这些单元格一定满足在 \(f\) 升序的情况下 \(A\) 降序的条件,可以使用单调队列维护。
每个单元格至多入队 \(2\) 次,且转移时直接取队首,时间复杂度 \(O(nm)\)。
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define endl '\n'
using namespace std;
typedef long long ll;template<typename T> void chkmin(T& x, T y) {if(y < x) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}const int inf = 0x3f3f3f3f;vector<vector<int>> calculate_moves(vector<vector<int>> A, int K) {int N = A.size(), M = A[0].size();rep(i, 0, N - 1) rep(j, 0, M - 1) --A[i][j];vector<int> r(N * M), c(N * M);rep(i, 0, N - 1) rep(j, 0, M - 1) r[A[i][j]] = i, c[A[i][j]] = j;vector<deque<int>> dqR(N), dqC(M);vector<int> dis(N * M, +inf);vector<vector<int>> R(N, vector<int>(M));dis[0] = 0;dqR[r[0]].push_back(dis[0]);dqC[c[0]].push_back(dis[0]);R[r[0]][c[0]] = 0;rep(i, 1, N * M - 1) {while(!dqR[r[i]].empty() && dqR[r[i]].front() < i - K) dqR[r[i]].pop_front();while(!dqC[c[i]].empty() && dqC[c[i]].front() < i - K) dqC[c[i]].pop_front();if(!dqR[r[i]].empty()) chkmin(dis[i], dis[dqR[r[i]].front()] + 1);if(!dqC[c[i]].empty()) chkmin(dis[i], dis[dqC[c[i]].front()] + 1);while(!dqR[r[i]].empty() && dis[dqR[r[i]].back()] >= dis[i]) dqR[r[i]].pop_back();while(!dqC[c[i]].empty() && dis[dqC[c[i]].back()] >= dis[i]) dqC[c[i]].pop_back();dqR[r[i]].push_back(i);dqC[c[i]].push_back(i);R[r[i]][c[i]] = dis[i] == +inf ? -1 : dis[i];}return R;
}#ifdef LOCALint main() {ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int N, M, K;cin >> N >> M >> K;vector<vector<int>> A(N, vector<int>(M));rep(i, 0, N - 1) rep(j, 0, M - 1) cin >> A[i][j];vector<vector<int>> R = calculate_moves(A, K);rep(i, 0, N - 1) rep(j, 0, M - 1) cout << R[i][j] << " \n"[j == M - 1];return 0;
}#endif