Prison Break题解
题目大意
一个机器人在一个n*m的矩阵中,每一格可能是空地、禁地、开关、或充电器。机器人每走一格消耗一点电量。空地可以随意经过,禁地不可以经过,开关在机器人经过时会被关闭,每个充电器可以为机器人补满一次电量。求关闭所有开关的最小初始电量
思路
直接求关闭所有开关的最小初始电量有亿点点困难,而题中能看出一些“最大值最小”的意味(见下图),因此考虑用二分

于是问题转化为:给定电量,能否完成目标
如果使用\(f_{i,j,S}\)表示当前位置为\((i,j)\)且开关和充电器走过的情况为\(S\)时所剩下的最多能量,那么要计算多少次呢?DP要\(15 \times 15 \times (1<<15)\),二分答案至少\(10\),乘上多组样例,亲测T
那怎么办呢?
其实矩阵中除了D格不能走,其他格子其实都可以随意走,只有Y格和G格有特殊通途(G格只能使用一次),而S格可以直接忽视,无需讨论,即只需要关心当前为F/Y/G时的答案
定义F点、Y点、G点为关键点,我们要求从F点出发经过所有Y点且最多使用G点一次(G点可以经过无数次,但只有指明往G走时才使用)所需要的最小电池。
为了方便,将坐标\((i,j)\)表示为m进制数\((ij)_m\)
假设电池容量为\(v\),我们首先求出所有关键点之间的最短距离(不要Floyd),然后用\(f_{i,S}\)表示当前在\(i\)点,关键点经过情况为\(S\)时所剩下的最大电量
则\(f_{i,S}=max\{f_{i,S}-dis_{i,j}\}\)。如果是充电器,则要使得能量变满
代码
# include <bits/stdc++.h>
using namespace std;
const int N = 16;
const int dx [ ] = { 0, 0, 1, -1 };
const int dy [ ] = { 1, -1, 0, 0 };
int n, m;
int g [ N ] [ N ];
int Fx, Fy, numY, Yx [ N ], Yy [ N ], numG, Gx [ N ], Gy [ N ], numP, Px [ N ], Py [ N ];
int dis [ N ] [ N ];
int d [ N ] [ 1 << N ];
bool _vis [ N * N + N ];
int _dis [ N * N + N ];
//求u到v的最小距离
int bfs ( int u, int v )
{
if ( u == v ) return 0;
memset ( _vis, 0, sizeof ( _vis ) );
memset ( _dis, 0x3f, sizeof ( _dis ) );
queue < int > que;
que.push ( u );
_dis [ u ] = 0;
_vis [ u ] = 1;
while ( ! que.empty ( ) )
{
int t = que.front ( ); que.pop ( );
int x = t / m, y = t % m;
for ( int i = 0; i < 4; i ++ )
{
int tx = x + dx [ i ], ty = y + dy [ i ], tt = tx * m + ty;
if ( 0 <= tx && tx < n && 0 <= ty && ty < m && ! _vis [ tt ] && g [ tx ] [ ty ] )
{
_dis [ tt ] = _dis [ t ] + 1;
_vis [ tt ] = 1;
que.push ( tt );
if ( tt == v ) return _dis [ tt ];
}
}
}
return -1;
}
bool isok ( int S )
{
for ( int i = 0; i <= numY; i ++ )
if ( ! ( S & 1 << i ) ) return 0;
return 1;
}
bool check ( int x )
{
memset ( d, -1, sizeof ( d ) );
d [ 0 ] [ 1 ] = x;
for ( int S = 1; S < ( 1 << numP ); S ++ )
for ( int i = 0; i < numP; i ++ )
if ( S & 1 << i && d [ i ] [ S ] != -1 )
for ( int j = 0; j < numP; j ++ )
if ( i != j && ! ( S & 1 << j ) && dis [ i ] [ j ] != - 1 )
if ( dis [ i ] [ j ] <= d [ i ] [ S ] )
{
if ( numY + 1 <= j && j <= numY + numG ) d [ j ] [ S | 1 << j ] = x;
else
{
d [ j ] [ S | 1 << j ] = max ( d [ j ] [ S | 1 << j ], d [ i ] [ S ] - dis [ i ] [ j ] );
if ( isok ( S | 1 << j ) ) return 1;
}
}
return 0;
}
int main()
{
while ( scanf ( "%d%d", & n, & m ) != EOF )
{
if ( n == 0 && m == 0) break;
numY = numG = 0;
for ( int i = 0; i < n; i ++ )
{
getchar();
for ( int j = 0; j < m; j ++ )
{
char x; scanf ( "%c", & x );
switch ( x )
{
case 'S' : g [ i ] [ j ] = 1; break;
case 'F' : g [ i ] [ j ] = 1, Fx = i; Fy = j; break;
case 'G' : g [ i ] [ j ] = 1, Gx [ numG ] = i; Gy [ numG ++ ] = j; break;
case 'Y' : g [ i ] [ j ] = 1, Yx [ numY ] = i; Yy [ numY ++ ] = j; break;
case 'D' : g [ i ] [ j ] = 0; break;
}
}
}
numP = 1 + numG + numY;
Px [ 0 ] = Fx, Py [ 0 ] = Fy;
for ( int i = 1; i <= numY; i ++ ) Px [ i ] = Yx [ i - 1 ], Py [ i ] = Yy [ i - 1 ];
for ( int i = numY + 1; i <= numY + numG; i ++ ) Px [ i ] = Gx [ i - 1 - numY ], Py [ i ] = Gy [ i - 1 - numY ];
for ( int i = 0; i < numP; i ++ )
for ( int j = i + 1; j < numP; j ++ )
dis [ i ] [ j ] = dis [ j ] [ i ] = bfs ( Px [ i ] * m + Py [ i ], Px [ j ] * m + Py [ j ] );
int ll = 0, rr = 345;
if ( ! check ( rr ) ) printf ( "-1\n" );
else
{
while ( ll < rr )
{
int mid = ll + rr >> 1;
if ( check ( mid ) ) rr = mid;
else ll = mid + 1;
}
printf ( "%d\n", rr );
}
}
return 0;
}

浙公网安备 33010602011771号