传送门
感觉是近年
N
O
I
P
NOIP
NOIP最难的一道,不亚于天天爱跑步和18年的
d
d
p
ddp
ddp
首先暴力比较好想,我们用
(
x
1
,
y
1
,
x
2
,
y
2
)
(x1,y1,x2,y2)
(x1,y1,x2,y2)表示指定棋子在
(
x
1
,
y
1
)
(x1,y1)
(x1,y1),空格在
(
x
2
,
y
2
)
(x2, y2)
(x2,y2)的最小步数
这么做是
O
(
q
∗
n
2
∗
m
2
)
O(q*n^2*m^2)
O(q∗n2∗m2)
仔细分析发现,很多状态是没有用的,有用的状态当且仅当空格在指定格子的旁边,这样制定格子才能动
于是定义
(
x
,
y
,
d
)
(x, y, d)
(x,y,d)表示指定格子在
(
x
,
y
)
(x,y)
(x,y),空格在它的哪个方向的最小步数
考虑如何转移,比如说想把指定格子上移一格,我们可以先把空格移动到上放,然后动指定格子
于是预处理
f
x
,
y
,
k
,
l
f_{x,y,k,l}
fx,y,k,l表示把
(
x
,
y
)
(x,y)
(x,y)k方向的空格移动到 l方向的最小步数
可以
b
f
s
bfs
bfs 出来,复杂度
O
(
n
2
∗
m
2
)
O(n^2*m^2)
O(n2∗m2)
然后每次询问需要将空格移动到指定格子的某个方向,最小步数任然可以
b
f
s
bfs
bfs 出来
最后跑一个
s
p
f
a
spfa
spfa 就可以了
最核心的一步就是把状态从
n
2
m
2
n^2m^2
n2m2去掉冗杂状态缩小到
4
∗
n
m
4*nm
4∗nm
#include<bits/stdc++.h>
#define N 35
#define M 10050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int fx[5] = {0, 1, 0, -1, 0};
int fy[5] = {0, 0, 1, 0, -1};
int rev[5] = {0, 3, 4, 1, 2};
int n, m, q, Mp[N][N];
int id[N][N][5], node;
int f[N][N][5][5];
bool exi[N][N];
struct Node{
int x, y, d;
Node(int _x = 0, int _y = 0, int _d = 0){ x = _x; y = _y; d = _d;}
} p[M];
int dis[M]; bool vis[M]; queue<int> qu;
bool can(int x, int y){ return (x>0 && x<=n && y>=0 && y<=m && Mp[x][y]);}
int bfs(int stx, int sty, int edx, int edy){
queue<Node> q; q.push(Node(stx, sty, 0));
while(!q.empty()){
Node now = q.front(); q.pop();
if(now.x == edx && now.y == edy) return now.d;
for(int i = 1; i <= 4; i++){
Node nxt(now.x + fx[i], now.y + fy[i], now.d + 1);
if(can(nxt.x, nxt.y) && !exi[nxt.x][nxt.y]){ q.push(nxt); exi[nxt.x][nxt.y] = true;}
}
} return -1;
}
void bfs2(int stx, int sty, int edx, int edy){
queue<Node> q; memset(exi, 0, sizeof(exi));
q.push(Node(stx, sty, 0)); exi[stx][sty] = exi[edx][edy] = true;
while(!q.empty()){
Node now = q.front(); q.pop();
for(int i = 1; i <= 4; i++){
if(edx + fx[i] == now.x && edy + fy[i] == now.y){
int pos = id[edx][edy][i];
qu.push(pos); vis[pos] = true; dis[pos] = now.d; break;
}
}
for(int i = 1; i <= 4; i++){
Node nxt(now.x + fx[i], now.y + fy[i], now.d + 1);
if(can(nxt.x, nxt.y) && !exi[nxt.x][nxt.y]){ q.push(nxt); exi[nxt.x][nxt.y] = true;}
}
}
}
int spfa(int edx, int edy){
while(!qu.empty()){
int now = qu.front(); qu.pop(); vis[now] = 0;
int x = p[now].x, y = p[now].y, k = p[now].d;
for(int i = 1; i <= 4; i++){
int nx = x + fx[i], ny = y + fy[i];
if(can(nx, ny)){
int nxt = id[nx][ny][rev[i]];
if(dis[nxt] > dis[now] + f[x][y][k][i] + 1){
dis[nxt] = dis[now] + f[x][y][k][i] + 1;
if(!vis[nxt]) qu.push(nxt), vis[nxt] = 1;
}
}
}
}
int ans = 1e9;
for(int i = 1; i <= 4; i++) ans = min(ans, dis[id[edx][edy][i]]);
if(ans == 1e9) return -1; return ans;
}
int main(){
n = read(), m = read(), q = read();
memset(f, 63, sizeof(f));
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) Mp[i][j] = read();
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){
if(Mp[i][j]) for(int k = 1; k <= 4; k++) if(can(i+fx[k], j+fy[k]))
for(int l = 1; l <= 4; l++) if(can(i+fx[l], j+fy[l])){
memset(exi, 0, sizeof(exi)); exi[i][j] = true;
int d = bfs(i+fx[k], j+fy[k], i+fx[l], j+fy[l]);
if(d != -1) f[i][j][k][l] = d;
}
}
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(Mp[i][j])
for(int k = 1; k <= 4; k++) id[i][j][k] = ++node, p[node] = Node(i, j, k);
while(q--){
int Ex = read(), Ey = read(), Sx = read(), Sy = read(), Tx = read(), Ty = read();
if(Sx == Tx && Sy == Ty){ puts("0"); continue;}
memset(dis, 63, sizeof(dis));
memset(vis, 0, sizeof(vis));
bfs2(Ex, Ey, Sx, Sy); cout << spfa(Tx, Ty) << '\n';
} return 0;
}