말랑말랑한 개발자 이야기

[백준 17836번] 공주님을 구해라! 본문

알고리즘/백준

[백준 17836번] 공주님을 구해라!

말랑너구리 2021. 6. 22. 00:28

[백준 17836번] 공주님을 구해라!

www.acmicpc.net/problem/17836

 

17836번: 공주님을 구해라!

용사는 마왕이 숨겨놓은 공주님을 구하기 위해 (N, M) 크기의 성 입구 (1,1)으로 들어왔다. 마왕은 용사가 공주를 찾지 못하도록 성의 여러 군데 마법 벽을 세워놓았다. 용사는 현재의 가지고 있는

www.acmicpc.net

 

 

 문제

 용사는 마왕이 숨겨놓은 공주님을 구하기 위해 (N, M) 크기의 성 입구 (1,1)으로 들어왔다. 마왕은 용사가 공주를 찾지 못하도록 성의 여러 군데 마법 벽을 세워놓았다. 용사는 현재의 가지고 있는 무기로는 마법 벽을 통과할 수 없으며, 마법 벽을 피해 (N, M) 위치에 있는 공주님을 구출해야만 한다.

 마왕은 용사를 괴롭히기 위해 공주에게 저주를 걸었다. 저주에 걸린 공주는 T시간 이내로 용사를 만나지 못한다면 영원히 돌로 변하게 된다. 공주님을 구출하고 프러포즈 하고 싶은 용사는 반드시 T시간 내에 공주님이 있는 곳에 도달해야 한다. 용사는 한 칸을 이동하는 데 한 시간이 걸린다. 공주님이 있는 곳에 정확히 T시간만에 도달한 경우에도 구출할 수 있다. 용사는 상하좌우로 이동할 수 있다.

 성에는 이전 용사가 사용하던 전설의 명검 "그람"이 숨겨져 있다. 용사가 그람을 구하면 마법의 벽이 있는 칸일지라도, 단숨에 벽을 부수고 그 공간으로 갈 수 있다. "그람"은 성의 어딘가에 반드시 한 개 존재하고, 용사는 그람이 있는 곳에 도착하면 바로 사용할 수 있다. 그람이 부술 수 있는 벽의 개수는 제한이 없다.

 우리 모두 용사가 공주님을 안전하게 구출 할 수 있는지, 있다면 얼마나 빨리 구할 수 있는지 알아보자.

 

 입력

 첫 번째 줄에는 성의 크기인 N, M 그리고 공주에게 걸린 저주의 제한 시간인 정수 T가 주어진다. 첫 줄의 세 개의 수는 띄어쓰기로 구분된다. (3 ≤ N, M ≤ 100, 1 ≤ T ≤ 10000)

 두 번째 줄부터 N+1번째 줄까지 성의 구조를 나타내는 M개의 수가 띄어쓰기로 구분되어 주어진다. 0은 빈 공간, 1은 마법의 벽, 2는 그람이 놓여있는 공간을 의미한다. (1,1)과 (N,M)은 0이다.

 

 출력

 용사가 제한 시간 T시간 이내에 공주에게 도달할 수 있다면, 공주에게 도달할 수 있는 최단 시간을 출력한다.

 만약 용사가 공주를 T시간 이내에 구출할 수 없다면, "Fail"을 출력한다.

 

 

 풀이

#include <iostream>
#include <queue>
using namespace std;

int N, M, T;
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};
int arr[100][100];
int cnt[100][100];
bool check[100][100];

int ff(int a, int b){
	queue<pair<int, int>> q;
	q.push(make_pair(a,b));
	check[a][b] = true;
	
	int target = 2147483647;
	while(!q.empty()){
		int a = q.front().first;
		int b = q.front().second;
		q.pop();
		
		if(a == N-1 && b == M-1){
			return min(target, cnt[a][b]);
		}
		
		if(arr[a][b] == 2){
			target = cnt[a][b] + (N-1) - a + (M-1) - b;
		}
		
		for(int k=0;k<4;k++){
			int aa = a + dx[k];
			int bb = b + dy[k];
			if(aa < 0 || aa >= N || bb < 0 || bb >= M) continue;
			if(check[aa][bb]) continue;
			if(arr[aa][bb] == 1) continue;
			
			q.push(make_pair(aa, bb));
			check[aa][bb] = true;
			cnt[aa][bb] = cnt[a][b] + 1;
		}
	}
	
	if(target != 2147483647) return target;
	else return -1;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	
	cin >> N >> M >> T;
	for(int i=0;i<N;i++){
		for(int j=0;j<M;j++){
			cin >> arr[i][j];
		}
	}
	
	int k = ff(0,0);
	if(k == -1) cout << "Fail";
	else{
		if(k <= T) cout << k;
		else cout <<"Fail";
	}
	return 0;
}

 늘 풀던 BFS 구현문제와 동일하다. 길찾기 문제에서 벽을 부술수 있는 그람(칼)이 추가된 것이다. 처음위치부터 BFS를 돌다가 칼을 먹고나면 방문처리가 된 곳 임에도 불구하고 다시 방문해야하는 상황이 발생하게 되는데 처음에 이렇게 생각했다가 다시 생각해보니 쉽게 접근이 가능했다. 칼을 먹고나면 벽을 부술 수 있는 횟수에 제한이 없기 때문에 그냥 목적지까지와의 유클리드거리를 구하면 끝이었다. 따라서 구현은 다음과 같다.

 

 1. 일단 처음 위치부터 BFS를 돌며 목적지까지 간다.

  1-1. 도중에 칼을 만나면 칼까지의 거리 + 칼에서 목적지까지 거리를 구하여 저장해둔다.

  1-2. 도중에 칼을 만나지 않으면, 그 BFS를 통해 목적지까지 간 거리가 최소의 거리이다.

 2. BFS를 통해 목적지에 도달한 거리와 1-1의 거리와 비교하여 최소값을 구한다.

 3. 그 값이 T시간을 넘지 않으면 그 값을 출력하고, T시간을 넘으면 Fail을 출력한다.

 

비슷한 유형을 많이 풀어봐서 그런지 이제 BFS 구현문제는 두뇌회전이 잘되는 것 같다.

 

 

'알고리즘 > 백준' 카테고리의 다른 글

[백준 1976번] 여행가자  (0) 2021.06.22
[백준 20040번] 사이클 게임  (0) 2021.06.22
[백준 4195번] 친구 네트워크  (0) 2021.06.22
[백준 10971번] 외판원 순회2  (0) 2021.06.22
[백준 18232번] 텔레포트 정거장  (0) 2021.06.21