알고리즘과 언어/c++

C++ ) 백준 3055 탈출

nextcoder 2022. 2. 16. 19:04

 

 

 

문제)

 

https://www.acmicpc.net/problem/3055

 

3055번: 탈출

사악한 암흑의 군주 이민혁은 드디어 마법 구슬을 손에 넣었고, 그 능력을 실험해보기 위해 근처의 티떱숲에 홍수를 일으키려고 한다. 이 숲에는 고슴도치가 한 마리 살고 있다. 고슴도치는 제

www.acmicpc.net

 

 

BFS가 같은 맵에서 2번 진행되어야 하는 문제다.

 

1. 젖은 물의 공간이 확장되며 (BFS)

2. 고슴도치의 최소 거리를 구해야 한다. (BFS)

 

 

구현 자체는 어렵지 않으나 3가지를 어떻게 해결할 것인지가 관건일 것 같다.

 

1. 고슴도치는 물에 젖게 될 곳에도 가면 안된다.

 

-> 젖은 물의 공간을 먼저 확장하고, 그 다음으로 고슴도치의 BFS를 진행함으로써 해결했다.

 

2. 서로 다른 지점에서의 BFS 두 가지를 진행하는 방법?

 

-> 한 지점에서의 BFS는 while 문을 통해서 queue가 empty가 될 때 까지 진행한다.

하지만 두 가지를 한꺼번에 하기위해서는 queue가 empty가 될 떄 까지 계속 진행되는 것이 아닌(물이 전체적으로 확장되는 것이 아닌), 물이 1번 확장되고 고슴도치가 갈 수 있는 길이 1번 확장되고를 번갈아 해야한다.

 

이 문제를 해결하기 위해서는 각각의 확장을 진행하기 전에 queue의 size를 구해서,

물이 1번 확장이 되고 queue에 담긴 나머지는 다음에 확장이 되도록 (고슴도치가 1번 확장이 된 후에) 큐에 남겨놓는 방법이다.

즉, bfs 전에 queue의 길이를 구해서 bfs가 한 칸씩만 확장되도록 한다.

 

3. 1번씩 확장하는 것을 반복할 때 탈출 조건

 

-> 고슴도치가 무사히 목적지에 도착했으면 값을 'W(in)'으로 바꾸도록 설정했다.

탈출 조건은 목적지가 D에서 W로 바뀌었거나, 고슴도치가 더 이상 갈 곳이 없을 때(Move.empty()) 탈출문을 나오도록 설정했다.

 

#include <iostream>
#include <queue>

using namespace std;

char map[50][50];
int Fcheckmap[50][50]; // 물의 방문 체크
int Mcheckmap[50][50]; // 고슴도치의 방문 체크

int dy[] = {0, 0, -1, 1};
int dx[] = {-1, 1, 0, 0};

int R, C;
int yy, xx; // D(목적지)의 y좌표, x좌표를 담을 곳
int Count = 0;


void Solve(queue <pair<int, int>> & Move , queue <pair<int, int>> & Flood){

	int Movesize;
	int Floodsize;
	int tmpy;
	int tmpx;
	pair<int, int> tmp;

	//cout << "flood" << endl;
	while (map[yy][xx] != 'W' && !Move.empty()) {

		//cout << "count is " << Count << endl;

		Movesize = Move.size(); // 미리 queue의 길이를 세어 길이 1만큼만 확장하도록
		Floodsize = Flood.size(); //미리 queue의 길이를 세어 길이 1만큼만 물에 젖도록

		while (!Flood.empty() && Floodsize)
		{
			Floodsize--;
			tmp = Flood.front();
			Flood.pop();
			for(int i = 0; i < 4; i++)
			{
				tmpy = tmp.first + dy[i];
				tmpx = tmp.second + dx[i];

				if (tmpy < 0 || tmpy >= R || tmpx < 0 || tmpx >= C || map[tmpy][tmpx] == 'X' || Fcheckmap[tmpy][tmpx] || map[tmpy][tmpx] == 'D')
					continue; // 맵을 넘어가거나, 돌을 만나거나, 이미 방문했거나, 목적지라면 진행하지 않는다.
//				cout << tmpy << " " << tmpx << endl;
				map[tmpy][tmpx] = '*'; // 물에 젖음을 미리 표시 (여기에는 가지 않게)
				Flood.push(make_pair(tmpy, tmpx)); // queue에 넣어준다.
				Fcheckmap[tmpy][tmpx] = 1; // 방문 표시한다.
			}
		}

		//cout << "move" << endl;
		while (!Move.empty() && Movesize)
		{
			Movesize--;
			tmp = Move.front();
			Move.pop();
			for(int i = 0; i < 4; i++)
			{
				tmpy = tmp.first + dy[i];
				tmpx = tmp.second + dx[i];

				if (tmpy < 0 || tmpy >= R || tmpx < 0 || tmpx >= C || map[tmpy][tmpx] == 'X' || map[tmpy][tmpx] == '*' || Mcheckmap[tmpy][tmpx])
					continue; // 맵을 넘어가거나, 돌을 만나거나, 물이 있거나, 이미 방문했다면 진행하지 않는다.

				//cout << tmpy << " " << tmpx << endl;

				if (map[tmpy][tmpx] == 'D')
					map[tmpy][tmpx] = 'W'; // 목적지에 도착했다면 'W'로 바꿔주어 탈출 조건을 만들어준다.
				Move.push(make_pair(tmpy, tmpx));
				Mcheckmap[tmpy][tmpx] = 1;
			}
		}
		Count++; // 몇 번 만에 목적지에 도착했는지 센다.
	}

	if (map[yy][xx] == 'W')
		cout << Count;
	else
		cout << "KAKTUS";

}
int main() {

	cin >> R >> C;

	queue <pair<int, int>> Move;
	queue <pair<int, int>> Flood;

	char tmp;


	for(int i = 0; i <R; i++)
	{
		for(int j = 0; j < C; j++)
		{

			cin >> tmp;
			if (tmp == 'S') {
				Move.push(make_pair(i, j)); // 고슴도치 좌표 queue에 넣기
				Mcheckmap[i][j] = 1;
			}
			if (tmp == '*') {
				Flood.push(make_pair(i, j)); // 젖은 지역 queue에 넣기
				Fcheckmap[i][j] = 1;
			}
			if (tmp == 'D')
			{
				yy = i;
				xx = j;
			}
			map[i][j] = tmp;
		}
	}

	Solve(Move, Flood);
}