JZOJ 6493. 【GDOI2020模拟03.04】迷宫
题解
这道题的题面比较玄学,乍一看还以为是道PJ难度的最短路。。。 其实,题目的设定十分有趣,你被放进了迷宫里,只能分清门(A/B/C/D),而不能分清房间(0除外),也就是说,你只知道当前可能所在的房间集合,和整个迷宫的结构,而并不知道具体在哪个房间,不过可以推算每一步可能所在的房间集合,希望最少的步数能保证走出这个迷宫(走出0号房间), 直接设
d
i
s
[
s
]
dis[s]
d i s [ s ] 为当前可能在的房间集合为
s
s
s 的最终答案,然后进行转移。 一种很自然的想法,每个状态
s
s
s 直接由四个状态转移过来,分别是走四扇门出去到达的房间集合,
d
i
s
[
s
]
=
m
i
n
(
d
i
s
[
s
1
]
,
d
i
s
[
s
2
]
,
d
i
s
[
s
3
]
,
d
i
s
[
s
4
]
)
+
1
dis[s]=min(dis[s1],dis[s2],dis[s3],dis[s4])+1
d i s [ s ] = m i n ( d i s [ s 1 ] , d i s [ s 2 ] , d i s [ s 3 ] , d i s [ s 4 ] ) + 1 然而仔细推敲过后,发现这样并不是正确的(答案可能更优) 以下是重点内容 我们不仅可以通过当前的房间集合和选择走出的门判断走到的房间集合,也可以通过走出后到达的门得出走到的房间集合, 简单地说,就是原本的四种转移(A/B/C/D)变成了十六种(A->A/A->B/A->C…D->D,分别为走出的门和走进的另一扇门), 不过,也不是直接在这十六种状态中取
m
i
n
min
m i n 更新,因为走进的另一扇门是在走出前不能确认的吗,也就是说,选择好了特定的一扇门走出后,才能通过走进的门得到当前可能在的四个集合,这四个是相互独立的,但都是在选择好了走出的门的前提下的, 因此还是要枚举选哪个门走出去,在走出去后的四种情况中取
m
a
x
max
m a x (注意,是
m
a
x
max
m a x ) ,作为转移的值,去更新
d
i
s
[
s
]
dis[s]
d i s [ s ] 。 具体实现需要分层DP,写的不好可能会被卡。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1050000
int st[ 25 ] [ 4 ] [ 2 ] ;
int a[ 25 ] , g[ N] [ 4 ] [ 4 ] ;
int last[ N] , nxt[ N* 16 ] , to[ N* 16 ] [ 4 ] , len= 0 ;
int dis[ N] , p[ N] , q[ N] , e[ N] , b[ 21 ] [ N] ;
void add ( int x, int A, int B, int C, int y)
{
to[ ++ len] [ 0 ] = y;
to[ len] [ 1 ] = A, to[ len] [ 2 ] = B, to[ len] [ 3 ] = C;
nxt[ len] = last[ x] ;
last[ x] = len;
}
int main ( )
{
int n, Q, i, j, k, x, y;
scanf ( "%d%d" , & n, & Q) ;
for ( i= 1 ; i<= 2 * n; i++ )
{
char c, d;
scanf ( "%d %c %d %c" , & x, & c, & y, & d) ;
st[ x] [ c- 'A' ] [ 0 ] = y, st[ x] [ c- 'A' ] [ 1 ] = d- 'A' ;
st[ y] [ d- 'A' ] [ 0 ] = x, st[ y] [ d- 'A' ] [ 1 ] = c- 'A' ;
}
st[ 0 ] [ 0 ] [ 0 ] = st[ 0 ] [ 1 ] [ 0 ] = st[ 0 ] [ 2 ] [ 0 ] = st[ 0 ] [ 3 ] [ 0 ] = 0 ;
st[ 0 ] [ 0 ] [ 1 ] = 0 , st[ 0 ] [ 1 ] [ 1 ] = 1 , st[ 0 ] [ 2 ] [ 1 ] = 2 , st[ 0 ] [ 3 ] [ 1 ] = 3 ;
for ( i= 0 ; i< 4 ; i++ )
for ( j= 0 ; j< 4 ; j++ ) g[ 1 ] [ i] [ j] = 1 ;
for ( i= 2 ; i< ( 1 << n) ; i++ )
{
int t= i, s= 0 ;
while ( t% 2 == 0 ) t/ = 2 , s++ ;
for ( j= 0 ; j< 4 ; j++ )
for ( k= 0 ; k< 4 ; k++ ) g[ i] [ j] [ k] = g[ i- ( 1 << s) ] [ j] [ k] ;
for ( j= 0 ; j< 4 ; j++ )
g[ i] [ j] [ st[ s] [ j] [ 1 ] ] | = 1 << st[ s] [ j] [ 0 ] ;
}
e[ 0 ] = 0 ;
for ( i= 1 ; i<= 1 << n; i++ ) if ( i% 2 ) e[ i] = e[ i/ 2 ] + 1 ; else e[ i] = e[ i/ 2 ] ;
for ( i= 1 ; i< 1 << n; i++ ) b[ e[ i] ] [ ++ b[ e[ i] ] [ 0 ] ] = i;
memset ( dis, 127 , sizeof ( dis) ) ;
dis[ 0 ] = 0 , dis[ 1 ] = 1 ;
for ( k= 1 ; k<= n; k++ )
{
for ( i= 2 ; i<= ( 1 << n) ; i++ )
{
if ( dis[ i] <= k) continue ;
dis[ i] = min ( dis[ i] , max ( max ( dis[ g[ i] [ 0 ] [ 0 ] ] , dis[ g[ i] [ 0 ] [ 1 ] ] ) , max ( dis[ g[ i] [ 0 ] [ 2 ] ] , dis[ g[ i] [ 0 ] [ 3 ] ] ) ) + 1 ) ;
dis[ i] = min ( dis[ i] , max ( max ( dis[ g[ i] [ 1 ] [ 0 ] ] , dis[ g[ i] [ 1 ] [ 1 ] ] ) , max ( dis[ g[ i] [ 1 ] [ 2 ] ] , dis[ g[ i] [ 1 ] [ 3 ] ] ) ) + 1 ) ;
dis[ i] = min ( dis[ i] , max ( max ( dis[ g[ i] [ 2 ] [ 0 ] ] , dis[ g[ i] [ 2 ] [ 1 ] ] ) , max ( dis[ g[ i] [ 2 ] [ 2 ] ] , dis[ g[ i] [ 2 ] [ 3 ] ] ) ) + 1 ) ;
dis[ i] = min ( dis[ i] , max ( max ( dis[ g[ i] [ 3 ] [ 0 ] ] , dis[ g[ i] [ 3 ] [ 1 ] ] ) , max ( dis[ g[ i] [ 3 ] [ 2 ] ] , dis[ g[ i] [ 3 ] [ 3 ] ] ) ) + 1 ) ;
}
}
while ( Q-- )
{
scanf ( "%d" , & x) ;
printf ( "%d
" , dis[ x] ) ;
}
return 0 ;
}