문제 링크 : https://www.acmicpc.net/problem/2573
아래 그림처럼 빙산의 높이가 NxM 이차원 배열에 주어진다.
빈 칸은 전부 빙산이 아니므로 0으로 주어진다.
빙산은 1년이 지날 때마다 빙산의 상하좌우에 인접한 곳에 0이 있다면 (빙산이 아닌 부분과 인접하면) 그 갯수만큼 녹는다.
이 때, 아래 그림은 한 덩어리로 되어 있는데 두 덩어리 이상으로 쪼개지기까지 걸리는 년수를 출력하면 된다.
만약 모든 빙산이 다 녹을 때까지 두 덩어리 이상이 되지 않으면 0을 출력한다.
N, M, map에 주어진 입력을 받고, map[i][j] 값이 0보다 큰 빙산이면 큐에 저장하도록 한다 (빙산이 아예 주어지지 않으면 result는 자연스럽게 0 출력)
이중반복문이 시작되고, 현재 빙산인 부분의 좌표와 높이를 저장하는 cur_ice 벡터와 지금 시점에서 1년 후에도 빙산인 부분의 좌표와 높이를 저장하는 next_ice 벡터를 정의한다. (그런데 실질적으론 next_ice와 cur_ice에 저장하는건 같고 next_ice 기준으로 높이를 체크해서 다음 큐에 담을 걸 걸러내긴 한다. 이 부분은 리팩토링 해야할 듯)
빙산 좌표와 높이를 담은 큐에서 현재 빙산 근처에 0이 있는지 (map[xx][yy] == 0인지?)를 체크하고 만약 있다면 녹기 전의 초기 높이(init_height, 초기값은 top.height)를 하나씩 줄이고(--) next_ice 벡터에 빙산의 좌표와 새로 녹은 값이 반영된 높이를 저장한다.
cur_q 큐를 선언해서 현재 방문중인 빙산 좌표중에 아무거나 하나를 집어넣고, 큐를 돌려서 시작점에서 방문하지 않았었고, 인접해있는 빙산(!visit[xx][yy] && map[xx][yy] > 0)들만 cur_q에서 다시 방문하도록 해서 지금이 한 덩어리인지 아닌지 체크한다.
cur_q 돌리는게 끝나면 포문으로 visit[cur_ice[i].x][cur_ice[i].y] 중에 한 좌표라도 방문되지 않았는지 체크하고, 방문되지 않은 빙산이 있다면 무조건 두 덩어리 이상이므로 현재 시간을 결과값에 저장, istwo = true로 할당하고 반복문을 빠져나오게 한다.
두 덩어리 이상이 아니라면 map 이차원 배열에 녹은 빙산의 높이들을 반영하고 빙산의 높이가 다시 0보다 큰 좌표들만 큐에 넣어서 위의 과정을 반복하도록 한다.
// 2573. 빙산
#define _CRT_SECURE_NO_WARINGS
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
struct iceberg {
int x;
int y;
int height;
};
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
int N, M;
int map[301][301];
int visit[301][301];
int result;
int cur_time;
queue<iceberg> Q;
bool istwo = false;
int main() {
cin >> N >> M;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++) {
cin >> map[i][j];
if (map[i][j] > 0) {
Q.push({ i, j, map[i][j] });
}
}
}
while (!Q.empty()) {
vector<iceberg> cur_ice;
vector<iceberg> next_ice;
memset(visit, 0, sizeof(visit));
while (!Q.empty()) {
iceberg top = Q.front();
cur_ice.push_back(top);
int init_height = top.height;
Q.pop();
/* 녹이는 작업 진행 */
for (int i = 0; i < 4; i++) {
int xx = top.x + dx[i];
int yy = top.y + dy[i];
if (xx < 1 || yy < 1 || xx > N || yy > M) continue;
if (map[xx][yy] == 0) init_height--;
}
next_ice.push_back({ top.x, top.y, init_height });
}
/*cur_q로 두 덩어리인지 아닌지 확인 */
queue<iceberg> cur_q;
cur_q.push(cur_ice[0]);
while (!cur_q.empty()) {
iceberg top = cur_q.front();
cur_q.pop();
visit[top.x][top.y] = 1;
for (int i = 0; i < 4; i++) {
int xx = top.x + dx[i];
int yy = top.y + dy[i];
if (xx < 1 || yy < 1 || xx > N || yy > M) continue;
if (!visit[xx][yy] && map[xx][yy] > 0) {
visit[xx][yy] = 1;
cur_q.push({ xx, yy, 0 });
}
}
}
for (int i = 0; i < cur_ice.size(); i++) {
if (visit[cur_ice[i].x][cur_ice[i].y] == 0) {
result = cur_time; istwo = true; break;
}
}
if (istwo) break;
else {
for (int i = 0; i < next_ice.size(); i++) {
int xx = next_ice[i].x;
int yy = next_ice[i].y;
if (next_ice[i].height <= 0) map[xx][yy] = 0;
else map[xx][yy] = next_ice[i].height;
if (next_ice[i].height > 0) Q.push(next_ice[i]);
}
}
cur_time++;
}
cout << result << endl;
}
처음에 문제 설계를 잘못해서 시행착오를 너무 많이 했다.
황당한 실수를 많이 했던 문제였지만, 대강 1년전의 내가 한 번 도전해서 '실패'만 띄워놓고 못 풀었던 문제를 1시간만에 풀어낸 것에 의의를 둬야겠다.
다음엔 같은 실수를 하지 않도록 주의하자.