[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 ;
}

猜你喜欢

转载自www.cnblogs.com/beretty/p/9579975.html