[NOIP2013]华容道
输入输出样例
输入样例#1:
3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2
输出样例#1:
2
-1
NOIP题好难啊QwQ
不会
暴力70分好打,正解emmmm
我们发现一个问题就是只有空白块在要求块的旁边(上下左右)时才能带着ta满图跑
所以我们用4个状态表示空白块在要求块的四个位置
*0 : 在上面
*1 : 在下面
*2 : 在左边
*3: 在右边
然后就是以状态为点,每种状态向可以变成的状态连边
最后跑最短路就行了
然而我从网上粘了个题解><
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
const int M = 35 ;
const int N = 4005 ;
const int W = 100005 ;
const int INF = 10000005 ;
const int dx[4] = { -1 , 1 , 0 , 0 } ;
const int dy[4] = { 0 , 0 , -1 , 1 } ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
struct Pos{ int x , y ; };
struct Node { int Id , dis ;};
inline bool operator < (Node a , Node b) { return a.dis > b.dis ; }
int hea[N] , num ;
struct E { int Nxt , to , Dis ; }edge[W];
inline void add_edge(int from , int to , int dis) {
edge[++num].Nxt = hea[from] ; edge[num].to = to ;
edge[num].Dis = dis ; hea[from] = num ;
}
int n , m , p[M][M] ;
int dis[M][M] ;
inline void Bfs(int Ex , int Ey , int Gx , int Gy , int Sit) {
queue< Pos > q ;
memset(dis , 0 , sizeof(dis)) ;
dis[Ex][Ey] = 1 ;
q.push((Pos) { Ex , Ey }) ;
while(!q.empty()) {
Pos temp = q.front() ; q.pop() ;
int x = temp.x , y = temp.y ;
for(int i = 0 ; i < 4 ; i ++) {
int Tx = x + dx[i] , Ty = y + dy[i] ;
if(Tx == Gx && Ty == Gy) continue ;
if(p[Tx][Ty] && !dis[Tx][Ty]) {
dis[Tx][Ty] = dis[x][y] + 1 ;
q.push((Pos){ Tx , Ty }) ;
}
}
}
// 先处理出一个格子周围的可移动的格子在不经过ta的情况下跑到其他位置的最短距离
if(Sit < 0) return ;
// 一个与(Gx,Gy)相邻的格子不经过(Gx,Gy)移动到其他与(Gx,Gy)相邻的格子
for(int i = 0 , Tx , Ty ; i < 4 ; i ++) {
Tx = Gx + dx[i] , Ty = Gy + dy[i] ;
if(Tx == Ex && Ty == Ey) continue ; if(!dis[Tx][Ty]) continue ;
add_edge(Gx * 120 + Gy * 4 + Sit , Gx * 120 + Gy * 4 + i , dis[Tx][Ty] - 1) ;
}
// 相邻的格子交换
add_edge(Gx * 120 + Gy * 4 + Sit , Ex * 120 + Ey * 4 + (Sit^1) , 1) ;
}
int Dis[N] ;
bool vis[N] ;
inline void Dijkstra (int Gx , int Gy) { // 起点
priority_queue < Node > q ;
memset(Dis , 63 , sizeof(Dis)) ;
memset(vis , false , sizeof(vis)) ;
for(int i = 0 , Tx , Ty , u ; i < 4 ; i ++) { // 从空的格子移动到初始格子附近
Tx = Gx + dx[i] , Ty = Gy + dy[i] ;
u = Gx * 120 + Gy * 4 + i ;
if(!dis[Tx][Ty]) continue ;
Dis[u] = dis[Tx][Ty] - 1 ;
q.push((Node){u , Dis[u]}) ;
}
while(!q.empty()) {
int u = q.top().Id ; q.pop() ;
if(vis[u]) continue ;
vis[u] = true ;
for(int i = hea[u] ; i ; i = edge[i].Nxt) {
int v = edge[i].to ;
if(!vis[v] && Dis[v] > Dis[u] + edge[i].Dis) {
Dis[v] = Dis[u] + edge[i].Dis ;
q.push((Node){v , Dis[v]}) ;
}
}
}
}
int main() {
n = read() ; m = read() ; int T = read() ;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= m ; j ++)
p[i][j] = read() ;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= m ; j ++) {
if(!p[i][j]) continue ;
for(int k = 0 ; k < 4 ; k ++) {
int x = i + dx[k] , y = j + dy[k] ;
if(p[x][y]) Bfs(x , y , i , j , k) ;
}
}
while(T -- ) {
int Ex = read() , Ey = read() , Sx = read() , Sy = read() , Tx = read() , Ty = read() ;
if(Sx == Tx && Sy == Ty) {printf("0\n") ; continue ;}
Bfs(Ex , Ey , Sx , Sy , -1) ; Dijkstra(Sx , Sy) ;
int Ans = INF ;
for(int i = 0 ; i < 4 ; i ++)
// 空的格子移动到了目标格子周围 , 所以初始格子已经到了目标格子
// 枚举4种情况取最小值
Ans = min(Ans , Dis[Tx * 120 + Ty * 4 + i]) ;
if(Ans < INF) printf("%d\n",Ans) ;
else printf("-1\n") ;
}
return 0 ;
}