https://www.acmicpc.net/problem/3055
문제 요약
사악한 암흑의 군주 이민혁은 드디어 마법 구슬을 손에 넣었고, 그 능력을 실험해보기 위해 근처의 티떱숲에 홍수를 일으키려고 합니다. 이 숲에는 고슴도치가 한 마리 살고 있습니다. 고슴도치는 제일 친한 친구인 비버의 굴로 가능한 빨리 도망가 홍수를 피하려고 합니다.
티떱숲의 지도는 R행 C열로 이루어져 있습니다. 비어있는 곳은 '.'로 표시되어 있고, 물이 차있는 지역은 '*', 돌은 'X'로 표시되어 있습니다. 비버의 굴은 'D'로, 고슴도치의 위치는 'S'로 나타내어져 있습니다.
매 분마다 고슴도치는 현재 있는 칸과 인접한 네 칸 중 하나로 이동할 수 있습니다. (위, 아래, 오른쪽, 왼쪽) 물도 매 분마다 비어있는 칸으로 확장합니다. 물이 있는 칸과 인접해있는 비어있는 칸(적어도 한 변을 공유)은 물이 차게 됩니다. 물과 고슴도치는 돌을 통과할 수 없습니다. 또, 고슴도치는 물로 차있는 구역으로 이동할 수 없고, 물도 비버의 소굴로 이동할 수 없습니다.
티떱숲의 지도가 주어졌을 때, 고슴도치가 안전하게 비버의 굴로 이동하기 위해 필요한 최소 시간을 구하는 프로그램을 작성해야합니다.
고슴도치는 물이 찰 예정인 칸으로 이동할 수 없습니다. 즉, 다음 시간에 물이 찰 예정인 칸으로 고슴도치는 이동할 수 없습니다. 이동할 수 있으면 고슴도치가 물에 빠지기 때문입니다.
첫째 줄에 고슴도치가 비버의 굴로 이동할 수 있는 가장 빠른 시간을 출력합니다. 만약, 안전하게 비버의 굴로 이동할 수 없다면, "KAKTUS"를 출력합니다.
문제 풀이
이전에 풀어보았던 불!과 거의 비슷한 맥락입니다.
문제의 핵심은 물이 지나간 노드의 가중치보다 고슴도치가 지나갈 가중치가 더 작으면 지나갈수 있다 입니다.
따라서 물 bfs와 고슴도치 bfs두번 ( 물이 많으면 2번 이상이지만 ) 을 돌리는게 중요합니다.
1. 맵을 입력받습니다 맵을 받으면서 고슴도치 위치, 물 위치, 비버 동굴 위치를 따로 저장합니다. 물은 배열로 저장하였습니다.
2. 물 먼저 bfs 탐색을 해줍니다. 가중치를 저장해둘 dist 배열을 1e9로 초기화 해두고 탐색합니다.
3. 방문체크는 따로 하지 않습니다. 그저 현재 탐색하려는 노드가 1e9가 아니면 방문한것이기 때문에 스킵합니다.
3. 물 bfs 탐색을 마치면 고슴도치 탐색을 해줍니다. 위에 써있는데로 현재 dist배열이 고슴도치의 가중치보다 값이 작으면 스킵 합니다. ( 물이 더 빨리 지나가는 경우임 )
4. 고슴도치의 가중치가 더 작으면 탐색을 이어갑니다.
5. 탐색을 마치면 비버 동굴의 위치의 가중치가 1e9 이면 ( 물이 막아서 고슴도치 bfs가 탐색 못함 ) " KAKTUS " 를 출력하고 아닐 경우 가중치를 출력합니다.
주의할 점
본인 코드에서 물 bfs 탐색 할때에
if (dist[ny][nx] <= dist[y][x] + 1)
{
continue;
}
이렇게 썼다가 메모리 초과가 났습니다.
방문 체크 미흡
코드
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
int dy[4] = { 0, 1, 0, -1 };
int dx[4] = { 1, 0, -1, 0 };
int r, c, hy, hx, by, bx;
vector<vector<char>>v;
// 물 위치 담을 배열
vector<pair<int, int>>w;
int dist[54][54];
void print2()
{
puts("");
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
cout << dist[i][j];
}
puts("");
}
puts("");
}
void bfs(int y, int x)
{
queue<pair<int, int>>q;
q.push({ y, x });
dist[y][x] = 0;
while (!q.empty())
{
int y = q.front().first;
int x = q.front().second;
q.pop();
for (int i = 0; i < 4; i++)
{
int ny = y + dy[i];
int nx = x + dx[i];
if (ny >= r || ny < 0 || nx >= c || nx < 0)
{
continue;
}
// 비버 동굴 까지 체크
if (v[ny][nx] != '.' && v[ny][nx] != 'D')
{
continue;
}
// 물이 더 빨리 도착한곳은 탐색 안함
if (dist[ny][nx] <= dist[y][x] + 1)
{
continue;
}
dist[ny][nx] = dist[y][x] + 1;
q.push({ ny, nx });
}
}
}
// 물 bfs
void bfsW(int y, int x)
{
queue<pair<int, int>>q;
q.push({ y, x });
dist[y][x] = 0;
while (!q.empty())
{
int y = q.front().first;
int x = q.front().second;
q.pop();
for (int i = 0; i < 4; i++)
{
int ny = y + dy[i];
int nx = x + dx[i];
if (ny >= r || ny < 0 || nx >= c || nx < 0)
{
continue;
}
if (v[ny][nx] != '.')
{
continue;
}
if (dist[ny][nx] != 1e9)
{
continue;
}
dist[ny][nx] = dist[y][x] + 1;
q.push({ ny,nx });
}
}
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> r>> c;
v.resize(r, vector<char>(c));
fill(&dist[0][0], &dist[r][c], 1e9);
for (int i = 0; i < r; i++)
{
string s;
cin >> s;
for (int j = 0; j < c; j++)
{
v[i][j] = s[j];
// 고슴도치 위치
if (v[i][j] == 'S')
{
hy = i;
hx = j;
}
// 물 위치
if (v[i][j] == '*')
{
w.push_back({ i, j });
}
// 비버 동굴 위치
if (v[i][j] == 'D')
{
by = i;
bx = j;
}
}
}
// 물 퍼지게
for (int i = 0; i < w.size(); i++)
{
bfsW(w[i].first, w[i].second);
}
//print2();
// 고슴도치 탐색
bfs(hy, hx);
//print2();
//
// 비버 동굴에 도달하지 못한 경우
if (dist[by][bx] == 1e9)
{
cout << "KAKTUS";
}
else
{
cout << dist[by][bx];
}
return 0;
}
예제 1
3 3
D.*
...
.S.
결과 1
예제 2
3 3
D.*
...
..S
결과 2
예제 3
3 6
D...*.
.X.X..
....S.
결과 3
예제 4
5 4
.D.*
....
..X.
S.*.
....
결과 4
'BAEKJOON > BFS' 카테고리의 다른 글
C++ / 2146 / 다리 만들기 ( 너비 우선 탐색 ) (0) | 2025.06.05 |
---|---|
C++ / 7569 / 토마토 ( 너비 우선 탐색 ) (0) | 2025.04.24 |
C++ / 5014 / 스타트링크 ( 너비 우선 탐색 ) (0) | 2025.02.12 |
C++ / 16948 / 데스 나이트 ( 너비 우선 탐색 ) (0) | 2024.12.31 |
C++ / 7562 / 나이트의 이동 ( 너비 우선 탐색 ) (1) | 2024.12.22 |