YTY说:“要有题解!” 于是,我就来写了
题目链接 :CodeVS 3290
题目大意
在一个
N×M
(
N,M≤30
) 的华容道棋盘里面,每一个棋子都是
1×1
的大小,有些格子是无法移动的。对于一个给出棋盘,有
Q
(
Q≤500
) 个询问 : 给出空白格(B),起点(S),终点(E),问最少步数。
分析
首先,让我们看看要怎么玩华容道 :
1. 把空白格子移动到S附近(多种决策,不经过S);
2. 把S移动到空白格内,如果S到达E,则结束;
3. 把空白格移动到下一个地方(多种决策,且在S移动后的格子附近),回到2。
显然,可以暴力
O(QN4)
, 显然,超时(60分)。
我们发现其实每次BFS都会做很多重复的搜索,于是我们可以先做一个预处理:对于每一个点,BFS预处理空白格子从他的i方向移动到j方向
(0≤i,j<4)
的最少步数,这样就可以跳过3的搜索。
之后,对于每一个询问
O(N2)
的BFS + SPFA得出答案,复杂度
O(N4+QKN2)
上代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <utility>
using namespace std ;
const int K = 4 ;
const int N = 30 + 2 ;
const int INF = 0x3f3f3f3f ;
const int dir[ K ][ 2 ] = { 1 , 0 , -1 , 0 , 0 , 1 , 0 , -1 } ; // 偏移,dir[0],dir[1]反向 dir[2],dir[3]反向
int n, m, q ;
bool plat[ N ][ N ] ;
int move[ N ][ N ][ K ][ K ] ; // 空白格从i方向到j方向的最短距离
struct node_move {
int x, y ;
int last ; // 空白格子对于此坐标的方向
inline void scan() {
scanf( "%d %d", &x, &y ) ;
}
inline node_move move( int to ) {
node_move b = { x + dir[ to ][ 0 ] , y + dir[ to ][ 1 ] , to ^ 1 } ;
return b ;
} // 把此状态向to方向移动,返回得到的新状态(注意新状态的空白格)
inline bool operator == ( const node_move a ) const {
if ( x == a.x && y == a.y ) return true ;
else return false ;
}
} B, S, E ;
#define Movv( a ) movv[ a.x ][ a.y ]
#define Bokk( a ) bokk[ a.x ][ a.y ]
#define Plat( a ) plat[ a.x ][ a.y ]
int movv[ N ][ N ] ;
bool bokk[ N ][ N ] ;
queue <node_move> Q1 ;
queue <node_move> Q2 ;
void init_move( int x , int y ) {
node_move center = { x , y , 0 } ;
if ( !Plat( center ) ) return ; // 判断格子是否合法
for ( int i = 0 ; i < K ; i ++ ) {
node_move temp = center.move( i ) ;
if ( !Plat( temp ) ) continue ;
Q1.push( temp ) ;
} // 加入四个方向,注意合法
while ( !Q1.empty() ) {
// 对每一个Q1中的点进行BFS,求到center四周的步数
node_move temp = Q1.front() ; Q1.pop() ;
memset( bokk , 0 , sizeof( bokk ) ) ;
memset( movv , 0x3f , sizeof( movv ) ) ;
Movv( temp ) = 0 ; Q2.push( temp ) ; Bokk( temp ) = true ;
while ( !Q2.empty() ) {
node_move tmp = Q2.front() ; Q2.pop() ;
for ( int i = 0 ; i < K ; i ++ ) {
node_move node = tmp.move( i ) ;
if ( !Bokk( node ) && Plat( node ) && !( node == center ) ) {
Movv( node ) = Movv( tmp ) + 1 ;
Q2.push( node ) ; Bokk( node ) = true ;
}
}
}
for ( int i = 0 ; i < K ; i ++ )
move[ center.x ][ center.y ][ temp.last ^ 1 ][ i ] = Movv( center.move( i ) ) ;
// temp.last^1 方向才是每次BFS起点!! (画重点)
}
}
void init() {
memset( move , 0x3f , sizeof( move ) ) ;
scanf( "%d %d %d", &n, &m, &q ) ;
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ ) {
int a ; scanf( "%d", &a ) ;
if ( a ) plat[ i ][ j ] = true ;
else plat[ i ][ j ] = false ;
}
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = 1 ; j <= m ; j ++ )
init_move( i , j ) ; // 预处理
}
// 每个node_move都相当与是图中的一个点,用dist记录距离和book记录入队
#define Dist( a ) dist[ a.x ][ a.y ][ a.last ]
#define Book( a ) book[ a.x ][ a.y ][ a.last ]
#define Move( a , b ) move[ a.x ][ a.y ][ a.last ][ b ]
int dist[ N ][ N ][ K ] ;
bool book[ N ][ N ][ K ] ;
void figure() {
for ( int i = 1 ; i <= q ; i ++ ) {
B.scan(), S.scan(), E.scan() ;
if ( !Plat( S ) || !Plat( E ) ) {
puts( "-1" ) ; continue ;
} // 起点和终点是否合法(貌似多余)
if ( S == E ) {
puts( "0" ) ; continue ;
} // 特判
memset( bokk , 0 , sizeof( bokk ) ) ;
memset( book , 0 , sizeof( book ) ) ;
memset( movv , 0x3f , sizeof( movv ) ) ;
memset( dist , 0x3f , sizeof( dist ) ) ;
Movv( B ) = 0 ; Q1.push( B ) ; Bokk( B ) = true ;
while ( !Q1.empty() ) {
node_move temp = Q1.front() ; Q1.pop() ;
for ( int j = 0 ; j < K ; j ++ ) {
node_move node = temp.move( j ) ;
if ( !Bokk( node) && Plat( node ) && !( node == S ) ) {
Movv( node ) = Movv( temp ) + 1 ;
Q1.push( node ) ; Bokk( node ) = true ;
}
}
} // BFS从B到S四周(不经过S)距离
for ( int j = 0 ; j < K ; j ++ ) {
if ( !Plat( S.move( j ) ) ) continue ;
Dist( S.move( j ) ) = Movv( S.move( j ) ) + 1 ;
Q1.push( S.move( j ) ) ; Book( S.move( j ) ) = true ;
} // 加入从S开始的决策
while ( !Q1.empty() ) {
node_move temp = Q1.front() ; Q1.pop() ;
for ( int j = 0 ; j < K ; j ++ ) {
node_move node = temp.move( j ) ;
if ( !Plat( node ) ) continue ;
if ( Dist( node ) > Dist( temp ) + Move( temp , j ) + 1 ) {
if ( !Book( node ) ) Book( node ) = true ;
Q1.push( node ) ;
Dist( node ) = Dist( temp ) + Move( temp , j ) + 1 ;
}
}
} // SPFA 起始状态到终点状态的最短路
int ans = INF ;
for ( int j = 0 ; j < K ; j ++ ) {
node_move temp = (node_move){ E.x , E.y , j } ;
ans = min( ans , Dist( temp ) ) ;
} // 可以从多个方向移动到E
if ( ans == INF ) ans = -1 ;
printf( "%d\n", ans ) ;
}
}
int main() {
init() ;
figure() ;
return 0 ;
}
以上