✔Algorithm/이것이 코딩 테스트다/그래프 이론/커리큘럼

yellow·2021년 5월 25일
0

알고리즘 문제

목록 보기
24/58
post-thumbnail

📖 문제

동빈이는 온라인으로 컴퓨터공학 강의를 듣고 이싿. 이때 각 온라인 강의는 선수 강의가 있을 수 있는데, 선수 강의가 있는 강의는 선수 강의를 먼저 들어야만 해당 강의를 들을 수 있다. 예를 들어 '알고리즘' 강의의 선수 강의로 '자료구조'와 '컴퓨터 기초'가 존재한다면, '자료구조'와 '컴퓨터 기초'를 모두 들은 이후에 '알고리즘' 강의를 들을 수 있다.
동빈이는 총 N개의 강의를 듣고자 한다. 모든 강의는 1번부터 N번까지의 번호를 가진다. 또한 동시에 여러 개의 강의를 들을 수 있다고 가정한다. 예를 들어 N = 3일 때, 3번 강의의 선수 강의로 1번과 2번 강의가 있고, 1번과 2번 강의는 선수 강의가 없다고 가정하자. 그리고 각 강의에 대하여 강의 시간이 다음과 같다고 가정하자.

  • 1번 강의 : 30시간
  • 2번 강의 : 20 시간
  • 3번 강의 : 40시간

이 경우 1번 강의를 수강하기까지의 최소 시간은 30시간, 2번 강의를 수강하기까지의 최소 시간은 20시간, 3번 강의를 수강하기까지의 최소 시간은 70시간이다.
동빈이가 듣고자 하는 N개의 강의 정보가 주어졌을 때, N개의 강의에 대하여 수강하기까지 걸리는 최소 시간을 각각 출력하는 프로그램을 작성하시오.

입력 조건

  • 첫째 줄에 동빈이가 듣고자 하는 강의의 수 N(1 <= N <= 500)이 주어진다.
  • 다음 N개의 줄에는 각 강의의 강의 시간과 그 강의를 듣기 위해 먼저 들어야 하는 강의들의 번호가 자연수로 주어지며, 각 자연수는 공백으로 구분한다. 이때 강의 시간은 100,000 이하의 자연수이다.
  • 각 강의 번호는 1부터 N까지로 구성되며, 각 줄은 -1로 끝난다.

출력 조건

  • N개의 강의에 대하여 수강하기까지 걸리는 최소 시간을 한 줄에 하나씩 출력한다.

📝 해설

  • 위상 정렬 알고리즘의 응용문제이다.
  • 각 강의에 대하여 인접한 노드를 확인할 때, 인접한 노드에 대하여 현재보다 강의 시간이 더 긴 경우를 찾는다면(answer[i] < answer[cur] + s_time[i]) 더 오랜 시간이 걸리는 경우의 시간 값을 저장하는 방식으로 결과 테이블을 갱신하여 답을 구한다.
  • 따라서 위상 정렬을 수행하면서, 매번 간선 정보를 확인하여 결과 테이블을 갱신한다.

⌨ 코드

from collections import deque

n = int(input())

# 강의의 강의 시간을 담는 리스트
s_time = [0] * (n + 1)
# 강의의 선수과목의 개수(진입 차수)를 담는 리스트
indegree = [0] * (n + 1)
# 강의들의 연결 정보를 담기 위한 리스트
graph = [[] for _ in range(n + 1)]

for i in range(1, n + 1):
  # i번 강의의 강의 시간과 선수과목들, 그리고 -1을 입력받아서 임시리스트에 저장
  tmp = list(map(int, input().split()))
  # i번 강의의 강의 시간 저장
  s_time[i] = tmp[0]
  for j in range(1, len(tmp) - 1):
    graph[tmp[j]].append(i)
    # 해당 과목 진입차수 +1 (선수과목 개수 +1)
    indegree[i] += 1

q = deque()
for i in range(1, n + 1):
  # 진입 차수가 0인 강의를 큐에 넣기
  if indegree[i] == 0:
    q.append(i)

# 정답을 담는 리스트
# 미리 해당 강의 수강 시간을 담아둔다.
answer = s_time[:]

# 큐가 빌 때까지 반복
while q:
  # cur : 현재 다루는 강의
  cur = q.popleft()

  # cur번 강의를 선수과목으로 둔 과목 i에 대해서
  for i in graph[cur]:
    # 진입 차수 -1
    indegree[i] -= 1
    # 더 오랜 시간이 걸리는 시간을 answer 리스트에 담는다.
    answer[i] = max(answer[i], answer[cur] + s_time[i])
    # 새로 진입 차수가 0이 된 과목 큐에 넣는다.
    if indegree[i] == 0:
      q.append(i)

for i in range(1, n + 1):
  print(answer[i])

☺ 느낀점

위상 정렬에 필요한 리스트나 변수들을 다 잘 만들어놓고 쓰다가 제일 결정적인 알고리즘을 잘못 세워서 책에 나온 답안 예시를 참고했다🥺

잘못된 생각

  • 예시) 4번 강의의 선수 과목은 1번과 3번일 때

    처음에는 위 그림의 1. 처럼 트리의 차수별로 계산되는 것처럼 생각해서 틀렸다..
    위 그림의 2. 처럼, 어차피 위상정렬에서 사용하는 인접 리스트에서는
    graph[4] = [1, 3]이 아닌 graph[1] = [2, 3, 4]로 표현하기 때문에
    4번 강의의 선수과목을 처리하는 경우, 먼저 1번 강의로 인해 answer[4]가 14로 변경되고, 다음 3번 강의로 인해 answer[4]가 18로 변경되는 과정을 이해해야 한다.
profile
할 수 있어! :)

0개의 댓글