电路维修
给你个n*m的格子方阵,格子上标有 \ 或者 / ,代表这个格子左上角与右下角联通 或者 右上角与左下角联通。 你可以选择旋转格子,使得它变成另外一种情况的联通。要你联通方阵最左上角和最右下角。
有多组数据。
解
可以将方格的角视作一个点,它通往隔壁的点如果不需要转格子,那么建边权值为0,需要转格子,那么权值为1.
于是就成了求最短路的问题。
但是普通地跑 SPFA 会挂掉。
于是改成双端队列求解(还跑得快一点)。
代码
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
int T, n, m, t, in[400000], Z[400000], l[400000];
char c;
struct asdf {
int bh, next, z;
} A[1000001];
void bfs() {
deque<int> Q;
Q.push_back(0); //加入队尾
while (!Q.empty()) {
//Spfa
int bh = Q.front();
Q.pop_front();
in[bh] = 1;
for (int i = l[bh]; i; i = A[i].next)
if (Z[bh] + A[i].z < Z[A[i].bh]) {
if (!in[A[i].bh]) {
//是否处理过了
if (A[i].z == 0) //如果权值为0,丢入队列头
Q.push_front(A[i].bh);
else //权值为1,丢入队列尾
Q.push_back(A[i].bh);
}
Z[A[i].bh] = Z[bh] + A[i].z; //更新值
}
}
}
void add(int x1, int y1, int x2, int y2, int z) {
//建边,我给每个点都赋了个编号...
int bh1 = x1 * (m+1) + y1;
int bh2 = x2 * (m+1) + y2;
A[++t] = (asdf){
bh2, l[bh1], z };
l[bh1] = t;
A[++t] = (asdf){
bh1, l[bh2], z };
l[bh2] = t;
}
void work() {
memset(Z, 0x7f, sizeof(Z));
memset(l, 0, sizeof(l));
memset(in, 0, sizeof(in));
t = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
//输入,建边
c = getchar();
while (c != '/' && c != '\\') c = getchar();
if (c == '/') {
add(i - 1, j - 1, i, j, 1);
add(i, j - 1, i - 1, j, 0);
} else {
add(i - 1, j - 1, i, j, 0);
add(i, j - 1, i - 1, j, 1);
}
}
Z[0] = 0;
bfs();
if (Z[(n + 1) * (m + 1) - 1] < 1000000) //输出答案
printf("%d\n", Z[(n + 1) * (m + 1) - 1]);
else
printf("NO SOLUTION\n");
}
int main() {
scanf("%d", &T); //多组数据。
while (T--) work();
}