[백준]1516 게임개발(위상정렬)

Ming·2022년 11월 8일
0

코테

목록 보기
11/11
post-thumbnail

문제

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

📝문제

숌 회사에서 이번에 새로운 전략 시뮬레이션 게임 세준 크래프트를 개발하기로 하였다. 핵심적인 부분은 개발이 끝난 상태고, 종족별 균형과 전체 게임 시간 등을 조절하는 부분만 남아 있었다.

게임 플레이에 들어가는 시간은 상황에 따라 다를 수 있기 때문에, 모든 건물을 짓는데 걸리는 최소의 시간을 이용하여 근사하기로 하였다. 물론, 어떤 건물을 짓기 위해서 다른 건물을 먼저 지어야 할 수도 있기 때문에 문제가 단순하지만은 않을 수도 있다. 예를 들면 스타크래프트에서 벙커를 짓기 위해서는 배럭을 먼저 지어야 하기 때문에, 배럭을 먼저 지은 뒤 벙커를 지어야 한다. 여러 개의 건물을 동시에 지을 수 있다.

편의상 자원은 무한히 많이 가지고 있고, 건물을 짓는 명령을 내리기까지는 시간이 걸리지 않는다고 가정하자.

입력

첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야 하는 건물들의 번호가 주어진다. 건물의 번호는 1부터 N까지로 하고, 각 줄은 -1로 끝난다고 하자. 각 건물을 짓는데 걸리는 시간은 100,000보다 작거나 같은 자연수이다. 모든 건물을 짓는 것이 가능한 입력만 주어진다.

출력

N개의 각 건물이 완성되기까지 걸리는 최소 시간을 출력한다.
예제입력1

5
10 -1
10 1 -1
4 1 -1
4 3 1 -1
3 3 -1

예제출력

10
20
14
18
17

풀이

이 문제는 어떤 건물을 짓기 위해서 다른 건물을 먼저 지어야 할 수도 있기 때문에 순서가 정해져 있는 작업이다. 이런 순서가 정해져 있는 작업을 차례로 수행해야할 때 그 순서를 결정해주기 위한 알고리즘이 위상정렬이다.

위상정렬

위상 정렬은 순서가 정해져 있는 작업을 차례로 수행해야 할 때 그 순서를 결정해주기 위해 사용하는 알고리즘이다. 위의 문제를 예시로 순서를 확인해보면 아래 그림과 같다.이때, 만약 사이클이 발생한다면 위상 정렬을 수행할 수 없다.

위상 정렬 : 1번 집->2번, 3번 집->4번, 5번 집
위상 정렬 알고리즘을 구현하는 방법에는 스택을 이용하는 방법과 큐를 이용하는 방법이 있다.

과정

과정
1. 진입차수가 0인 정점을 큐/스택에 삽입한다.
2. 큐/스택에서 원소를 꺼내고 해당 원소와 연결된 모든 간선을 제거한다.
3. 간선 제거 이후 진입차수가 0인 정점을 큐/스택에 삽입한다.
4. 2번~3번 과정을 큐/스택에 더 이상 원소가 없을 때까지 반복한다.
5. 모든 원소를 방문하기 전에 큐/스택가 빈다면 사이클이 존재하는 것이고 모든 원소를 방문했다면 큐/스택에서 꺼낸 순서가 위상 정렬 결과이다.

그래프에서 1번 집이 진입차수가 0이기 때문에 큐에 해당 정점을 삽입한다.
queue = [1]
큐에서 원소를 꺼내고 해당 원소와 연결된 모든 간선을 제거한다. 즉, 1번집에서 2, 3번집으로 연결된 간선을 제거한다. 진입차수가 0인 2, 3번 정점을 큐에 삽입한다.

queue=[2, 3]
answer=[1]
큐에서 2번 원소를 꺼내고 2번 원소와 연결된 모든 간선을 제거한다.
queue=[3]
answer=[1, 2]
이 예시에는 2번과 연결된 간선이 없기 때문에 생략하고 3번 원소를 꺼낸다. 4, 5번 정점과 연결된 간선을 제거하고 진입차수가 0인 정점을 큐에 삽입한다.

queue=[4, 5]
answer=[1, 2, 3]
위 처럼 2, 3번 과정을 반복하면 큐가 비어있게 되고 answer=[1, 2, 3, 4, 5]가 된다. 이 결과값이 위상정렬 값이다.

🖥문제 구현

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

n=int(input())
building=[[] for _ in range(n+1)]
degree=[0]*(n+1) #진입차수
cost=[0]*(n+1)
answer=[0]*(n+1)
q=deque()
for i in range(1, n+1) :
    data=list(map(int, input().split()))
    cost[i]=data[0]
    for data in data[1:-1] :
        building[data].append(i)
        degree[i]+=1

for i in range(1, n+1):
    if degree[i]==0:
        q.append(i)
        answer[i]=cost[i]

while q :
    x=q.popleft()
    for i in building[x] :
        degree[i]-=1
        answer[i]=max(answer[i], answer[x]+cost[i])
        if degree[i]==0:
            q.append(i)
for i in range(1, n+1):
    print(answer[i])

0개의 댓글