115. 커리큘럼

아현·2021년 7월 4일
0

Algorithm

목록 보기
116/400
post-custom-banner
  • 각 온라인 강의는 선수 강의가 있을 수 있는데, 선수 강의가 있는 강의는 선수 강의를 먼저 들어야만 해당 강의를 들을 수 있다.

    • 예를 들어 '알고리즘' 강의의 선수 강의로 '자료구조'와 '컴퓨터 기초'가 존재한다면, '자료구조'와 '컴퓨터 기초'를 모두 들은 이후에 '알고리즘' 강의를 들을 수 있다.
  • 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개의 강의에 대하여 수강하기까지 걸리는 최소 시간을 한 줄에 하나씩 출력한다.



1. 위상 정렬 알고리즘을 이용한 풀이


from collections import deque
import copy

v = int(input())
#모든 진입차수 0으로 초기화
indegree = [0] * (v + 1)
graph = [[] for i in range(v + 1)]
#각 강의 시간 0으로 초기화
time = [0] * (v + 1)

for i in range(1, v + 1):
  data = list(map(int, input().split()))
  time[i] = data[0] #첫 번째 수는 시간 정보
  for x in data[1:-1]:
    indegree[i] += 1 #진입차수 증가
    graph[x].append(i)

def topology_sort():
  #수행 결과를 담을 리스트
  result = copy.deepcopy(time)
  q = deque()

  #처음 시작은 진입차수가 0인 노드를 큐에 삽입
  for i in range(1, v + 1):
    if indegree[i] == 0:
      q.append(i)

  #큐가 빌 때까지 반복
  while q:
    #큐에서 원소 꺼내기
    now = q.popleft()

    for i in graph[now]:
      result[i] = max(result[i], result[now] + time[i]) #현재보다 강의 시간이 오래걸리는 경우 시간 값을 저장
      indegree[i] -= 1 #진입차수 감소
      #새롭게 진입차수가 0이 되는 노드를 큐에 삽입
      if indegree[i] == 0:
        q.append(i)

  for i in range(1, v + 1):
    print(result[i])

topology_sort()






  • 각 노드에 대하여 인접한 노드를 확인할 때, 인접한 노드에 대하여 현재보다 강의 시간이 더 긴 경우를 찾는다면, 더 오랜 시간이 걸리는 경우의 시간 값을 저장하는 방식으로 결과 테이블을 갱신하여 답을 구할 수 있다.

    • 위상 정렬을 수행하면서, 매번 간선 정보를 확인하여 결과 테이블을 갱신한다.
  • 최종적으로 각 강의를 수강하기까지의 최소 시간을 result 리스트 변수에 담도록 하였다.

  • 처음에 각 강의 시간은 time 리스트 변수에 담겨 있는데, 위상 정렬 함수의 초기 부분에서 deepcopy() 함수를 이용하여 time리스트 변수의 값을 복사하여 result 리스트 변수의 값으로 설정하는 작업이 수행된다.

    • 리스트의 경우 단순히 대입 연산을 하면 값이 변경될 때 문제가 발생할 가능성이 있기에, 리스트의 값을 복제해야 할 때는 deepcopy() 함수를 사용한다.



2. C++ 코드



#include <bits/stdc++.h>

using namespace std;

// 노드의 개수(V)
int v;
// 모든 노드에 대한 진입차수는 0으로 초기화
int indegree[501];
// 각 노드에 연결된 간선 정보를 담기 위한 연결 리스트 초기화
vector<int> graph[501];
// 각 강의 시간을 0으로 초기화
int times[501]; 

// 위상 정렬 함수 
void topologySort() {
    vector<int> result(501); // 알고리즘 수행 결과를 담을 리스트 
    for (int i = 1; i <= v; i++) {
        result[i] = times[i];
    } 
    
    queue<int> q; // 큐 라이브러리 사용

    // 처음 시작할 때는 진입차수가 0인 노드를 큐에 삽입
    for (int i = 1; i <= v; i++) {
        if (indegree[i] == 0) {
            q.push(i);
        }
    }

    // 큐가 빌 때까지 반복
    while (!q.empty()) {
        // 큐에서 원소 꺼내기
        int now = q.front();
        q.pop();
        // 해당 원소와 연결된 노드들의 진입차수에서 1 빼기
        for (int i = 0; i < graph[now].size(); i++) {
            result[graph[now][i]] = max(result[graph[now][i]], result[now] + times[graph[now][i]]);
            indegree[graph[now][i]] -= 1;
            // 새롭게 진입차수가 0이 되는 노드를 큐에 삽입
            if (indegree[graph[now][i]] == 0) {
                q.push(graph[now][i]);
            }
        }
    }

    // 위상 정렬을 수행한 결과 출력
    for (int i = 1; i <= v; i++) {
        cout << result[i] << '\n';
    }
}

int main(void) {
    cin >> v;

    // 방향 그래프의 모든 간선 정보를 입력받기
    for (int i = 1; i <= v; i++) {
        // 첫 번째 수는 시간 정보를 담고 있음 
        int x;
        cin >> x;
        times[i] = x;
        // 해당 강의를 듣기 위해 먼저 들어야 하는 강의들의 번호 입력 
        while (true) {
            cin >> x;
            if (x == -1) break;
            indegree[i] += 1;
            graph[x].push_back(i);
        }
    }

    topologySort();
}



3. Java 코드



import java.util.*;

public class Main {

    // 노드의 개수(V)
    public static int v;
    // 모든 노드에 대한 진입차수는 0으로 초기화
    public static int[] indegree = new int[501];
    // 각 노드에 연결된 간선 정보를 담기 위한 연결 리스트 초기화
    public static ArrayList<ArrayList<Integer>> graph = new ArrayList<ArrayList<Integer>>();
    // 각 강의 시간을 0으로 초기화
    public static int[] times = new int[501];

    // 위상 정렬 함수
    public static void topologySort() {
        int[] result = new int[501]; // 알고리즘 수행 결과를 담을 배열
        for (int i = 1; i <= v; i++) {
            result[i] = times[i];
        }

        Queue<Integer> q = new LinkedList<>(); // 큐 라이브러리 사용

        // 처음 시작할 때는 진입차수가 0인 노드를 큐에 삽입
        for (int i = 1; i <= v; i++) {
            if (indegree[i] == 0) {
                q.offer(i);
            }
        }

        // 큐가 빌 때까지 반복
        while (!q.isEmpty()) {
            // 큐에서 원소 꺼내기
            int now = q.poll();
            // 해당 원소와 연결된 노드들의 진입차수에서 1 빼기
            for (int i = 0; i < graph.get(now).size(); i++) {
                result[graph.get(now).get(i)] = Math.max(result[graph.get(now).get(i)], result[now] + times[graph.get(now).get(i)]);
                indegree[graph.get(now).get(i)] -= 1;
                // 새롭게 진입차수가 0이 되는 노드를 큐에 삽입
                if (indegree[graph.get(now).get(i)] == 0) {
                    q.offer(graph.get(now).get(i));
                }
            }
        }

        // 위상 정렬을 수행한 결과 출력
        for (int i = 1; i <= v; i++) {
            System.out.println(result[i]);
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        v = sc.nextInt();

        // 그래프 초기화
        for (int i = 0; i <= v; i++) {
            graph.add(new ArrayList<Integer>());
        }

        // 방향 그래프의 모든 간선 정보를 입력받기
        for (int i = 1; i <= v; i++) {
            // 첫 번째 수는 시간 정보를 담고 있음 
            int x = sc.nextInt();
            times[i] = x;
            // 해당 강의를 듣기 위해 먼저 들어야 하는 강의들의 번호 입력 
            while (true) {
                x = sc.nextInt();
                if (x == -1) break;
                indegree[i] += 1;
                graph.get(x).add(i);
            }
        }

        topologySort();
    }
}

profile
For the sake of someone who studies computer science
post-custom-banner

0개의 댓글