[백준] 14868 - 문명 (P4)

2025. 4. 24. 16:18·알고리즘 (PS)/BOJ
반응형

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

 

풀이 과정

시드마이어의 문명과 비슷한 게임을 구현하는 시뮬레이션 문제

문명이 건설되었을 때, 시간이 지남에 따라 점점 퍼지다가 두 문명이 인접하면 하나의 문명으로 합쳐진다.

이때 모든 문명이 하나로 합쳐지기까지 걸리는 시간을 구하는 문제이다.

 

문명이 건설되었을 때 퍼지는 것은 BFS로 구현할 수 있고, 문명을 하나의 그룹으로 합치는 것은 분리집합으로 구현할 수 있다.

(분리집합을 사용하지 않고도 풀 수 있다는데, 그렇게는 안 풀어봤다.)

 

from collections import deque
import sys
input = sys.stdin.readline

def find(x):
    if parent[x] == x:
        return x

    parent[x] = find(parent[x])
    return parent[x]

def union(x1, x2):
    global group_count
    x1 = find(x1)
    x2 = find(x2)
    if x1 != x2:
        parent[x1] = x2
        group_count -= 1

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

 

우선 BFS, Union Find 를 사용할 때 필요한 변수와 함수를 만들어준다.

 

n, k = map(int, input().split())
board = [[-1 for _ in range(n)] for _ in range(n)]
parent = [i for i in range(k)]
group_count = k
year = 0

 

다음으로 입력을 받는데, 중요한 것은 group_count 변수이다.

union find 에서 union 연산을 수행할 때마다 group 개수는 1개씩 줄어든다.

 

문제에서 구하는 모든 문명이 하나로 합쳐지는 시간은 group_count 값이 1이 되기까지의 시간을 말한다.

year 은 그 시간(년수)에 대한 변수이다.

 

나는 정말 나이브하게 문제를 풀었다.

문제를 읽어보면

 

1. 모든 문명은 매년 상하좌우로 인접한 곳으로 퍼지고

2. 퍼진 곳 또는 퍼진 곳과 상하좌우 인접한 곳에 다른 문명이 있으면 해당 문명과 하나로 합쳐진다

 

라고 했다.

 

즉 한 해 동안 일어나는 일은

 

1. 인접한 곳으로 퍼지기 

2. 인접 문명과 합치기

 

가 일어나는 것이다.

그리고 인접 문명과 합치기 과정을 끝냈을 때 group_count  = 1 이면 그때까지의 시간을 출력하면 된다.

 

이때 처음에 문명을 입력받고, 해당 문명에 대해서 인접한 문명끼리는 바로 합쳐야 하는데,

이 부분만 따로 떼어내서 구현하고, 그 뒤로는 반복문을 통해 구현하였다.

 

d = deque()
for i in range(k):
    x, y = map(int, input().split())
    d.append((x-1, y-1))
    board[x-1][y-1] = i

for now_x, now_y in d:
    for k in range(4):
        next_x, next_y = now_x + dx[k], now_y + dy[k]
        if 0 <= next_x < n and 0 <= next_y < n:
            if board[next_x][next_y] != -1:
                union(board[next_x][next_y], board[now_x][now_y])

 

BFS를 돌 때 board 를 -1로 초기화했다가 문명이 퍼지면 해당 문명 번호로 board 를 채워서 board 배열 값이 -1 인지 아닌지로 visit 여부를 체크했다.

먼저 문명 별 위치 정보를 받아서 board 에 해당 문명 번호를 표시하고, 덱에도 추가해준다.

그리고 (덱의 원소를 빼지 않고) 덱을 순회하면서 0년도 문명 상태에 대해서 인접 문명들을 하나로 합쳐준다.

 

while group_count > 1:
    new_d = deque()
    for now_x, now_y in d:
        for k in range(4):
            next_x, next_y = now_x + dx[k], now_y + dy[k]
            if 0 <= next_x < n and 0 <= next_y < n:
                if board[next_x][next_y] == -1:
                    new_d.append((next_x, next_y))
                    board[next_x][next_y] = board[now_x][now_y]  # 현재 문명으로 방문

    for now_x, now_y, now_year in new_d:
        for k in range(4):
            next_x, next_y = now_x + dx[k], now_y + dy[k]
            if 0 <= next_x < n and 0 <= next_y < n:
                if board[next_x][next_y] != -1:
                    union(board[next_x][next_y], board[now_x][now_y])

    year += 1
    d = new_d

 

다음으로 group_count 가 1보다 큰 동안 반복을 돈다.

만약 0년도 문명 상태에 대해서 인접 문명들이 모두 하나로 합쳐졌다면 group_count = 1 이 되어 반복문을 돌지 않는다.

 

1. 문명 퍼뜨리기

나는 매 년도마다 문명 상태를 따로 관리하기 위해서 새로운 년도에 대해서 새로운 덱을 사용하여 순회하였다.

기존 덱을 순회하면서 문명들을 퍼뜨리고, 이렇게 새로 퍼뜨려진 문명 정보는 기존 덱이 아니라 새로운 덱에 추가한다.

그래야 퍼지기 전에 존재했던 영역과 이번에 새로 퍼진 영역을 구분할 수 있기 때문이다.

 

2. 인접 문명 체크하기

새롭게 퍼진 영역 (new_d) 를 순회하면서 각 새로운 영역의 인접한 곳에 기존 문명이 있는지 확인한다.

기존 문명이 있다면 union 연산을 통해 하나로 합쳐준다.

 

이 과정이 끝나면 연도가 하나 증가하고,

기존의 덱을 새로 만든 덱으로 교체한다.

 

이 과정을 group_count > 1 인 동안에 계속 반복하고, group_count = 1 이 되면 반복문을 나와서 year 값을 출력하면 된다.

 


파이썬 정답 코드

from collections import deque
import sys
input = sys.stdin.readline

def find(x):
    if parent[x] == x:
        return x

    parent[x] = find(parent[x])
    return parent[x]

def union(x1, x2):
    global group_count
    x1 = find(x1)
    x2 = find(x2)
    if x1 != x2:
        parent[x1] = x2
        group_count -= 1

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

n, k = map(int, input().split())
board = [[-1 for _ in range(n)] for _ in range(n)]
parent = [i for i in range(k)]
group_count = k
year = 0

d = deque()
for i in range(k):
    x, y = map(int, input().split())
    d.append((x-1, y-1, 1))
    board[x-1][y-1] = i

for now_x, now_y, now_year in d:
    for k in range(4):
        next_x, next_y = now_x + dx[k], now_y + dy[k]
        if 0 <= next_x < n and 0 <= next_y < n:
            if board[next_x][next_y] != -1:
                union(board[next_x][next_y], board[now_x][now_y])

while group_count > 1:
    new_d = deque()
    for now_x, now_y in d:
        for k in range(4):
            next_x, next_y = now_x + dx[k], now_y + dy[k]
            if 0 <= next_x < n and 0 <= next_y < n:
                if board[next_x][next_y] == -1:
                    new_d.append((next_x, next_y))
                    board[next_x][next_y] = board[now_x][now_y]  # 현재 문명으로 방문

    for now_x, now_y in new_d:
        for k in range(4):
            next_x, next_y = now_x + dx[k], now_y + dy[k]
            if 0 <= next_x < n and 0 <= next_y < n:
                if board[next_x][next_y] != -1:
                    union(board[next_x][next_y], board[now_x][now_y])

    year += 1
    d = new_d

print(year)

 

 

C++ 정답 코드

#include <iostream>
#include <deque>
using namespace std;

typedef long long ll;

ll n, k, group_count;
ll board[2000][2000];
ll parent[100000]; k = {0 , 1, 2, . ... k}

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

void print_board() {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cout << board[i][j] << " ";
        }
        cout << '\n';
    }
}

ll find(ll x) {
    if (x == parent[x]) {
        return x;
    }

    return parent[x] = find(parent[x]);
}

void merge(ll x, ll y) {
    x = find(x);
    y = find(y);

    if (x != y) {
        parent[x] = y;
        group_count--;
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    /**
     * init
     */
    cin >> n >> k;
    deque<pair<ll, ll>> d;
    group_count = k;

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            board[i][j] = -1;
        }
    }

    for (int i = 0; i < k; i++) {
        parent[i] = i;
    }

    /**
     * logic
     */

    for (int i = 0; i < k; i++) {
        int x, y;
        cin >> x >> y;
        board[x-1][y-1] = i;
        d.emplace_back(x-1, y-1);
    }

    for (auto p : d) {
        for (int k = 0; k < 4; k++) {
            ll next_x = p.first + dx[k];
            ll next_y = p.second + dy[k];
            if (0 <= next_x && next_x < n && 0 <= next_y && next_y < n) {
                if (board[next_x][next_y] != -1) {
                    merge(board[next_x][next_y], board[p.first][p.second]);
                }
            }
        }
    }

    int year = 0;
    while (group_count > 1) {
        // 확장
        deque<pair<ll, ll>> new_d;
        for (auto p : d) {
            for (int k = 0; k < 4; k++) {
                ll next_x = p.first + dx[k];
                ll next_y = p.second + dy[k];
                if (0 <= next_x && next_x < n && 0 <= next_y && next_y < n) {
                    if (board[next_x][next_y] == -1) {
                        board[next_x][next_y] = board[p.first][p.second];
                        new_d.emplace_back(next_x, next_y);
                    }
                }
            }
        }

        for (auto p : new_d) {
            for (int k = 0; k < 4; k++) {
                ll next_x = p.first + dx[k];
                ll next_y = p.second + dy[k];
                if (0 <= next_x && next_x < n && 0 <= next_y && next_y < n) {
                    if (board[next_x][next_y] != -1) {
                        merge(board[next_x][next_y], board[p.first][p.second]);
                    }
                }
            }
        }

        d = new_d;
        year += 1;
    }

    cout << year << '\n';

    return 0;
}
반응형
저작자표시 비영리 변경금지 (새창열림)

'알고리즘 (PS) > BOJ' 카테고리의 다른 글

[백준] 1876 - 튕기는 볼링공  (0) 2025.03.29
[백준] 1248 - Guess (G3)  (0) 2025.03.20
[백준] 1166 - 선물  (0) 2024.11.13
[백준] 4315 - 나무 위의 구슬 (Python)  (0) 2024.11.12
[백준] 4436 - 엘프의 검  (0) 2024.11.09
'알고리즘 (PS)/BOJ' 카테고리의 다른 글
  • [백준] 1876 - 튕기는 볼링공
  • [백준] 1248 - Guess (G3)
  • [백준] 1166 - 선물
  • [백준] 4315 - 나무 위의 구슬 (Python)
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (615)
      • 개인 프로젝트 (43)
        • 토이 프로젝트 (3)
        • [2020] 카카오톡 봇 (9)
        • [2021] 코드악보 공유APP (22)
        • [2022] 유튜브 뮤직 클론코딩 (9)
        • [2025] 한글 SQL 데이터베이스 (0)
      • 팀 프로젝트 (22)
        • [2020] 인공지능 숫자야구 (4)
        • [2022] OSAM 온라인 해커톤 (10)
        • [2024] GDSC 프로젝트 트랙 (6)
        • [2025] 큰소리 웹 페이지 (2)
      • CS (335)
        • 자료구조 (19)
        • 어셈블리 (41)
        • 멀티미디어응용수학 (7)
        • 컴퓨터 구조 (29)
        • 알고리즘 분석 (4)
        • 컴퓨터 네트워크 (38)
        • 프로그래밍언어론 (15)
        • HCI 윈도우즈프로그래밍 (26)
        • 기초데이터베이스 (29)
        • 운영체제 (23)
        • 오토마타 (24)
        • 문제해결기법 (11)
        • 블록체인 (22)
        • 소프트웨어공학 (21)
        • 기계학습심화 (12)
        • 컴퓨터그래픽스와 메타버스 (8)
        • 분산시스템특론 (6)
      • 자기계발 (45)
        • 생각 정리 (23)
        • 대외활동 (11)
        • 동아리 (7)
        • 자격증 (3)
        • 머니 스터디 (1)
      • 알고리즘 (PS) (107)
        • BOJ (101)
        • Programmers (5)
        • 알고리즘 이모저모 (1)
      • WEB(BE) (8)
        • express.js (1)
        • Spring & Spring Boot (7)
      • WEB(FE) (2)
        • html, css, js (1)
        • React.js (1)
      • Tool & Language (6)
        • Edit Plus (1)
        • Git (1)
        • Python3 (2)
        • Java (2)
      • Infra (12)
        • AWS (1)
        • Oracle Cloud (8)
        • Firebase (2)
        • Network (1)
      • Android (18)
        • Java (6)
        • Flutter (12)
      • Window (2)
        • Visual Studio 없이 WPF (1)
        • MFC (1)
      • 독서 (14)
        • Inside Javascript (7)
        • Database Internals (6)
        • 한 글 후기 (1)
  • 링크

    • github
    • website
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에버듀
[백준] 14868 - 문명 (P4)
상단으로

티스토리툴바