双端队列BFS
对于图中只存在边权为\(0, 1\)的边,我们可以考虑双端队列优化Dijkstra, 即我们用一个双端队列来维护Dijkstra中堆的性质,具体做法如下:
如果当前更新边的边权为\(0\),那么我们可以将其插入到队首
如果边权为\(1\)我们可以将其插入到队尾
对于上面的做法, 我们可以得到一个BFS序列,证明双端队列合法等同于证明取出的数一定是最小的数,我们可以用归纳法证明:
假设当前的队列为
我们将\(x\)取出,那么它生成的\(x + 1\)将会到队尾,\(x\)将会到队头, 显然当\(x + 2\)出现时队列中不存在\(x\), 所以我们可以保证BFS序列的单调性和二段性,也就能够保证取出的数一定时最小的数。
双端队列BFS将Dijkstra算法优化到了\(O(m)\)
经典例题
我们可以将每次改变线路置为边权\(1\), 不交换则边权为\(0\), 那么我们便可以在上面跑Dijkstra来求到\((0, 0)->(r, c)\)的最短路,对于网格图来说,点数等于\(N = (r + 1) \times (c + 1)\), 边数可以当成\(M = nm \times 4\)算, 复杂度则为\(O(TMlogN)\), 但是由于存在5个样例,可能会超时,但是由于边权只存在\(0\)和\(1\),因此我们可以使用双端队列来优化。
另外这里处理的时候应该将格子和点分开讨论,具体如下
我们从\((1, 1)可以向(0, 0), (0, 2), (2, 2), (2, 0)\)方向走, 对应的格子转移应该为\([1, 1], [1, 2], [2, 2], [2, 1]\),因此我们的\(dx = [-1, -1, 1, 1], dy = [-1, 1, 1, -1]\), 对应的格子转移\(ix = [0, 0, 1, 1], iy[] = [0, 1, 1, 0]\), 对应的转移字符数组为g = "\\/\\/"
, 这里需要对\
转义, 复杂度就变为了\(O(Tm)\)
Code
#include <iostream>
#include <deque>
#include <cstring>
#define ff first
#define ss second
using i64 = long long;
int r, c, dist[600][600];
char a[600][600];
int dx[] = {-1, -1, 1, 1}, dy[] = {-1, 1, 1, -1};
int ix[] = {0, 0, 1, 1}, iy[] = {0, 1, 1, 0};
bool st[600][600];
/*
考虑每次交换边权为1, 那么问题转化为从(1, 1) 到 (n, m) 的最短路
因为边权只有0和1,所以我们可以考虑双端队列优化的dijkstra
基本原理为, 将堆 改为 双端队列,插入时如果边权为1,那么把更新的点插入到队尾, 如果边权为0把更新的点插入到队头
保证了每次出堆的都是离源点最近的点
*/
int bfs() {
memset(dist, 0x3f, sizeof dist);
memset(st, false, sizeof st);
std::deque<std::pair<int, int>> q1;
q1.push_front({0, 0});
dist[0][0] = 0;
while (q1.size()) {
auto t = q1.front(); q1.pop_front();
int x = t.ff, y = t.ss;
if (st[x][y])
continue;
st[x][y] = true;
char g[5] = "\\/\\/";
for (int i = 0; i <= 3; i ++) {
int tx = x + dx[i], ty = y + dy[i];
if (tx < 0 || ty < 0 || tx > r || ty > c) continue;
int gx = x + ix[i], gy = y + iy[i];
int v = !(a[gx][gy] == g[i]);
if (dist[tx][ty] > dist[x][y] + v) {
dist[tx][ty] = dist[x][y] + v;
if (v) {
q1.push_back({tx, ty});
} else {
q1.push_front({tx, ty});
}
}
}
}
return dist[r][c] == 0x3f3f3f3f?-1:dist[r][c];
}
void solve() {
std::cin >> r >> c;
for (int i = 1; i <= r; i ++) {
for (int j = 1; j <= c; j ++) {
std::cin >> a[i][j];
}
}
if (r + c & 1) {
puts("NO SOLUTION");
} else {
int k = bfs();
if (k == -1) {
puts("NO SOLUTION");
} else std::cout << k << "\n";
}
}
int main() {
int _;
std::cin >> _;
while (_ --) {
solve();
}
}