문제 해결에 사용된 개념
- 위상정렬 :
– 방향 그래프에서 각 점들을 순서대로 나열하는 방법
– 조건 : DAG(Directed Acyclic Graph) 그래프 방향성이 있고 사이클이 없는 그래프
– DFS를 이용한 방법과 진입차수(Indegree)를 이용한 2가지 방법이 있으나 ACM Craft에서는 건설 순서를 결정하기 위해 Indegree를 이용한 위상 정렬을 사용 - 다이나믹 프로그래밍 :
– 작은 문제들을 해결한 결과로 큰 문제를 해결하는 방법
– 중복된 계산을 줄이는 목적으로 사용됨
– ACM Craft에서는 각 도시의 건설 비용을 누적 하는데 사용됨 - 큐(Queue)
– FIFO(First In First Out) 자료 구조로 위상정렬을 구현하는데 사용
– 도시의 indegree가 0인 도시부터 처리하기 위해 큐 사용
사용된 매서드 및 시퀀스
- 위상정렬 시퀀스
– 인접 리스트 형식으로된 그래프에서 각 노드별 진입차수(indegree) 기록
– 큐 2개 생성 (탐색큐, 결과큐)
– 탐색큐에서 노드를 하나씩 poll하여 결과큐에 삽입 => 진입차수(indegree)가 0 이되면 정렬이 완료 된 것
– 탐색큐에서 poll한 노드와 연결된 노드들의 진입차수(indegree)를 -1해줌
– 진입차수 수정 후 0이 된다면 다시 탐색을 위해 탐색큐에 push
– 위 과정을 반 - void Input()
– 건물 개수와 규칙 개수를 ReadLine()
– 각 배열을 설정함 - int Output()
– Queue<int>를 사용해서 위상 정렬 수행 (FIFO)
– indegree가 0인 도시부터 시작하여, 연결된 도시들은 indegree를 감소 시키고 0이 되면 큐에 추가
– 각 도시의 최대 건설 비용 갱신
class ACM { //https://velog.io/@cpsn6237/3-BOJ-1005-ACM-Craft-C private int n, k; // 총 도시, 총 규칙 개수 private int targetCity; // 도착 도시 private int[] cityCost; // 각 도시 건설 시간 private int[] dpCost; // 각 도시의 효율적인 건설 시간 private int[] indegree; // 도시의 필요한 규칙 개수 private List<int>[] nextCities; // 도시의 다음 간선(목록) private List<int>[] prevCities; // 도시의 이전 간선(목록) public void Input() { // 2. 건물개수 N , 규칙개수 K int[] input = Array.ConvertAll(Console.ReadLine().Split(), int.Parse); int N = input[0]; int K = input[1]; // 3. 배열 사이즈 정의 cityCost = new int[N + 1]; dpCost = new int[N + 1]; indegree = new int[N + 1]; nextCities = new List<int>[N + 1]; prevCities = new List<int>[N + 1]; // 4. 각 도시의 다음/이전 간선 리스트 초기화 for (int i = 1; i <= n; i++) { nextCities[i] = new List<int>(); prevCities[i] = new List<int>(); } // 5. 각 도시의 건설 시간 입력받아 저장 var costInput = Array.ConvertAll(Console.ReadLine()!.Split(), int.Parse); for (int i = 1; i <= n; i++) { cityCost[i] = costInput[i - 1]; } // 6. 각 간선 정보 입력받아 nextCities, prevCities, indegree 배열에 반영 for (int i = 0; i < k; i++) { var edgeInput = Array.ConvertAll(Console.ReadLine()!.Split(), int.Parse); nextCities[edgeInput[0]].Add(edgeInput[1]); prevCities[edgeInput[1]].Add(edgeInput[0]); indegree[edgeInput[1]]++; } // 7. 목표 도착 도시 입력받기 targetCity = int.Parse(Console.ReadLine()!); } public int Output() { var q = new Queue<int>(); // indegree가 0인 도시를 큐에 추가 for (int i = 1; i <= n; i++) { if (indegree[i] == 0) q.Enqueue(i); } // 큐가 빌 때까지 반복 while (q.Count > 0) { var curCity = q.Dequeue(); // 이전 도시 중 가장 건설이 오래걸렸던 누적 비용 계산 var maxDP = 0; foreach (int node in prevCities[curCity]) { maxDP = Math.Max(maxDP, dpCost[node]); } dpCost[curCity] = maxDP + cityCost[curCity]; // 다음 도시의 indegree를 감소시키고, indegree가 0이 되면 큐에 추가 foreach (var next in nextCities[curCity]) { indegree[next]--; if (indegree[next] == 0) { q.Enqueue(next); } } } // 목표 도착 도시의 누적 건설 비용 반환 return dpCost[targetCity]; } } class Program { static void Main(string[] args) { // 1. 테스트 케이스 int testcase = int.Parse(Console.ReadLine()); while (testcase > 0) { var acm = new ACM(); acm.Input(); var result = acm.Output(); Console.WriteLine(result); } } }