-
C++ ) 백준 3055 탈출알고리즘과 언어/c++ 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); }
'알고리즘과 언어 > c++' 카테고리의 다른 글
C++ ) 백준 2003 수들의 합 2 (0) 2022.03.09 C++ ) 백준 17090 미로 탈출하기 (0) 2022.02.17 C++) 백준 1780 종이의 개수 (0) 2022.02.08 C++) 원하는 자리수 까지 출력하기 (반올림, 올림, 내림) (0) 2022.02.07 ios_base::sync_with_stdio(false); cin.tie(NULL); (0) 2021.11.14