📖 백준 16236번 : https://www.acmicpc.net/problem/16236

| 시간 제한 | 메모리 제한 |
|---|---|
| 2 초 | 512 MB |
N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다.
아기 상어와 물고기는 모두 크기를 가지고 있고, 이 크기는 자연수이다. 가장 처음에 아기 상어의 크기는 2이고, 아기 상어는 1초에 상하좌우로 인접한 한 칸씩 이동한다.
아기 상어는 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없고, 나머지 칸은 모두 지나갈 수 있다. 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다. 따라서, 크기가 같은 물고기는 먹을 수 없지만, 그 물고기가 있는 칸은 지나갈 수 있다.
아기 상어가 어디로 이동할지 결정하는 방법은 아래와 같다.
아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다. 즉, 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.
아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다. 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.
공간의 상태가 주어졌을 때, 아기 상어가 몇 초 동안 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는지 구하는 프로그램을 작성하시오.
첫째 줄에 공간의 크기 N(2 ≤ N ≤ 20)이 주어진다.
둘째 줄부터 N개의 줄에 공간의 상태가 주어진다. 공간의 상태는 0, 1, 2, 3, 4, 5, 6, 9로 이루어져 있고, 아래와 같은 의미를 가진다.
아기 상어는 공간에 한 마리 있다.
첫째 줄에 아기 상어가 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는 시간을 출력한다.
최단 경로를 반복해서 탐색해야하기 때문에 bfs를 여러번 사용하는 구현 문제로 접근했다. 만만하게 생각하고 풀기 시작한 것에 비해서 상당히 까다로운 문제다. 거리가 가까운 물고기가 여러 마리 존재 했을 때의 경우를 나누는 것이 꽤나 복잡하다. 범위 내에서 bfs 탐색을 하는 것은 여타 다른 문제들과 동일하지만, 아기 상어의 크기, 먹이를 먹은 횟수, 좌표, 지금까지 움직인 거리를 저장해야한다. 상당히 여러 가지의 정보를 다뤄야 하기 때문에 struct를 하나 만들어서 앞서 말한 정보들을 전부 담았다.
상어의 위치에서 탐색을 시작해 먹이의 위치를 찾고 거리를 저장한다. 이 거리에 해당하는 다른 먹이가 있는지 계속해서 탐색하고 저장한다. 탐색이 끝났으면 먹이들 중 가장 위쪽에 있고 같다면 가장 왼쪽에 있는 물고기를 먹고 먹이를 먹은 횟수를 갱신한다. 이 후 먹이를 먹은 좌표에서부터 다시 bfs를 하는 과정을 반복한다. 반복하다가 더 이상 먹을 먹이가 없는 경우를 체크해서 탐색을 완전히 종료하고 답을 출력하면된다.
chkDist를 사용해서 먹이의 거리를 고정하고 priority_queue를 이용해서 가장 위쪽, 가장 왼쪽의 먹이를 먹는 조건을 만족하는 위치를 찾는다. priority_queue의 compare함수를 직접 구현해서 조건을 만족하는 값을 top에 위치시켰다. 이 후 main에서 bfs를 반복해주고 pq에 값이 들어오지 않은 경우 즉, 더이상 먹을 먹이가 없는 경우에 탐색을 종료하고 답을 출력하면 끝이다. 너무 구현이 짜증나고 어려운 문제였다..
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
struct shark { int x; int y; int size; int prey; int dist; };
struct compare {
bool operator()(shark& a, shark& b) {
if (a.y == b.y) {
return a.x > b.x;
}
return a.y > b.y;
}
};
int n;
int dy[4] = { -1,0,0,1 };
int dx[4] = { 0,-1,1,0 };
int ans = 0;
int space[20][20];
bool visited[20][20];
shark bfs(shark start) {
fill(visited[0], visited[n], false);//재탐색을 위해서 visited값을 초기화
priority_queue<shark, vector<shark>, compare> pq;
queue<shark> q;
q.push(start);
visited[start.y][start.x] = true;
int chkDist = 999999;
while (!q.empty()) {
shark now = q.front();
q.pop();
if (chkDist < now.dist) break;
for (int i = 0; i < 4; i++) {
int x = now.x + dx[i];
int y = now.y + dy[i];
shark next = { x, y, now.size, now.prey, now.dist + 1 };
if (x >= 0 && x < n && y >= 0 && y < n && !visited[y][x]) {
//범위 내에 존재하고 방문하지 않은 경우
if (space[y][x] == 0 || space[y][x] == next.size) {
// 0이거나 사이즈가 동일한 곳일 때는 그냥 탐색
visited[y][x] = true;
q.push(next);
}
else if (space[y][x] < next.size) {
// 사이즈가 더 작은 먹이가 존재할 때
if (chkDist >= next.dist){
//chkDist를 갱신시키고 이보다 큰 값을 가지는 먹이는 탐색하지 않는다.
chkDist = next.dist;
visited[y][x] = true;
pq.push(next);
}
}
}
}
}
if (!pq.empty()) {
//pq는 가장 위쪽에 위치하고 같다면 가장 왼쪽에 있는 값을 top에 위치시킨다.
shark next = pq.top();
next.prey++;
if (next.prey == next.size) {
next.size++;
next.prey = 0;
}
space[next.y][next.x] = 0;
return next;
}
else {
shark c;
c.dist = 0;
return c;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
int a, b;
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> space[i][j];
if (space[i][j] == 9) {
a = j;
b = i;
space[i][j] = 0;
}
}
}
shark next = { a,b,2,0,0 };
while (true) {
next = bfs(next);
ans += next.dist;
if (next.dist == 0) break;//더 이상 먹이가 없는 경우
else next.dist = 0;// 재탐색을 위해서 next.dist를 0으로 초기화
}
cout << ans;
return 0;
}