[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 ;
}
posted @ 2018-09-03 18:21  beretty  阅读(157)  评论(0编辑  收藏  举报