문제
백준 16933
설명
각 위치에 대한 방문체크만 하면 틀린다. 같은 위치라도 남은 폭탄의 갯수에 따라 다른 결과를 얻을 수 있기 때문이다. 그러므로 방문 시 폭탄의 여부를 체크해서 더 많은 폭탄을 가지고 있을 때만 같은 위치에 중복으로 갈 수 있게 한다. 예를들어 int visitTB[1000][1000]
이런식으로 구성할 수 있겠다. 방문 체크 시 남은 폭탄에 해당하는 차원을 추가해 bool visitTB[1000][1000][11]
이런식으로 처리할 수도 있지만, 이 경우는 모든 경우를 계산하게 되어 느리다.
낮에는 쉴 필요가 없다. 그러므로 낮 밤 모두 쉬는걸 넣지 말자.
방문체크용 배열을 따로 만들기에는 공간이 너무 아까웠다. 그래서 board[]
의 맨 앞비트만 0
, 1
여부를 사용하고, 나머지 비트는 방문 시 가진 폭탄의 최태갯수를 넣었다.
시간 복잡도
O(\(\mathrm{N}\mathrm{M}\mathrm{K}\))
코드
int w , h , k ;
unsigned char board [ 1001 * 1001 ];
int dx []{ - 1 , 1 , 0 , 0 }, dy []{ 0 , 0 , 1 , - 1 };
struct E {
E ( int pos , int nBomb ) : pos ( pos ), nBomb ( nBomb ) {}
int pos ;
int nBomb = 0 ;
};
int main ()
{
fastio ;
cin >> h >> w >> k ;
for ( int i = 0 ; i < w * h ; i ++ )
{
char tmp ;
cin >> tmp ;
board [ i ] = tmp - '0' ;
}
int nMove = 0 ;
queue < E > q ;
q . push ( E ( 0 , k ));
while ( ! q . empty ())
{
int size = q . size ();
bool day = ( ++ nMove % 2 );
for ( int i = 0 ; i < size ; i ++ )
{
auto tmp = q . front (); q . pop ();
if ( tmp . pos == w * h - 1 ) {
cout << nMove ;
return 0 ;
}
int cur_x = tmp . pos % w , cur_y = tmp . pos / w ;
for ( int j = 0 ; j < 4 ; j ++ )
{
int x = cur_x + dx [ j ], y = cur_y + dy [ j ], d = y * w + x ;
if ( x < 0 || y < 0 || x >= w || y >= h ) continue ;
// board => [leftBomb+1][isOne] 이므로 맨 앞비트를 제거 후 남은 폭탄 갯수 비교
// 이때 board 의 기본값이 -1 이 될 수 없으므로 nBomb 에 1을 더해 nBomb == 0 의 경우 걸러지는걸 막음
if (( board [ d ] >> 1 ) >= tmp . nBomb + 1 ) continue ;
if ( board [ d ] & 0b1 ) // 이 위치가 가 `1` 일 때
{
if ( tmp . nBomb > 0 && day ) // 폭탄이 있고 낮일 때만 간다.
{
q . push ( E ( d , tmp . nBomb - 1 ));
board [ d ] = ( tmp . nBomb + 1 << 1 ) | 0b1 ;
}
}
else { // 이 위치가 `0` 일 때
q . push ( E ( d , tmp . nBomb ));
board [ d ] = ( tmp . nBomb + 1 << 1 );
}
}
if ( ! day ) q . push ( E ( tmp . pos , tmp . nBomb )); // 밤에만 쉬어도 충분하다.
}
}
cout << - 1 ;
}
댓글남기기