ACM Craft
백준 [1005] ACM Craft
풀이 1. 메모이제이션 + dfs 활용
건설하고자 하는 건물의 사전 건물들의 건설 시간을 탐색한다. 이미 확인된 건물의 시간은 탐색하지 않고 사전에 탐색한 시간을 활용한다.
- 참고 : 건물을 짓는 시간이 모두 0초 인 경우를 고려하여 건설 여부를 -1로 세팅한다. 0초로 세팅하는 경우 무한루프를 돌아 시간초과 발생
소스코드 1.
import java.util.*;
public class Main {
    public static void main(String[] agrs) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        for (int tc = 0; tc < T; tc++) {
            int N = sc.nextInt(); // 건물의 갯수
            int K = sc.nextInt(); // 건설 규칙 갯수
            // 건물 별 건설 시간
            int[] D = new int[N];
            for (int i = 0; i < N; i++) {
                D[i] = sc.nextInt();
            }
            // 건물 순서 규칙 입력 (key : 건물 번호, value: 건물이 지어지기 위해 사전에 지어져야하는 건물들)
            Map<Integer, List<Integer>> roleMap = new HashMap<>();
            for (int xy = 0; xy < K; xy++) {
                int x = sc.nextInt() - 1;
                int y = sc.nextInt() - 1;
                roleMap.computeIfAbsent(y, key -> new ArrayList<>()).add(x);
            }
            int W = sc.nextInt() - 1; // 건설하고자 하는 건물 번호
            int[] buildTime = new int[N];
            Arrays.fill(buildTime, -1);
            int buildingTime = getBuildingTime(roleMap, D, buildTime, W);
            System.out.println(buildingTime);
        }
    }
    private static int getBuildingTime(Map<Integer, List<Integer>> roleMap, int[] D, int[] buildTime, int currentNo) {
        // 건물의 시작점에 도달한 경우
        if (!roleMap.containsKey(currentNo)) {
            buildTime[currentNo] = D[currentNo];
            return buildTime[currentNo];
        }
        // 건물이 이미 지어진 경우
        if (buildTime[currentNo] != -1) {
            return buildTime[currentNo];
        }
        List<Integer> beforeBuildings = roleMap.get(currentNo);
        int maxBeforeBuildTime = 0;
        for (int beforeNo : beforeBuildings) {
            // 아직 사전 건물의 시간을 모르는 경우
            if (buildTime[beforeNo] == -1) {
                buildTime[beforeNo] = getBuildingTime(roleMap, D, buildTime, beforeNo);
            }
            maxBeforeBuildTime = Math.max(maxBeforeBuildTime, buildTime[beforeNo]);
        }
        buildTime[currentNo] = maxBeforeBuildTime + D[currentNo];
        return buildTime[currentNo];
    }
}
풀이 2. 메모이제이션 + 위상정렬 활용
건설 시작점이 되는 건물을 찾아 위상정렬 알고리즘을 활용한다.
- 참고 : 위상정렬은 사이클이 발생하지 않을때만 사용 가능하다. 
- [queue 구현 성능 차이 참고] 첫번째 줄 : ArrayDeque 사용, 두번째 줄 : LinkedList 사용 
소스코드 2.
import java.util.*;
public class Main {
    public static void main(String[] argd) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        for (int tc = 0; tc < T; tc++) {
            int N = sc.nextInt(); // 건물의 갯수
            int K = sc.nextInt(); // 건설순서 규칙 갯수
            int[] D = new int[N]; // 건물 당 건설 시간
            for (int d = 0; d < N; d++) {
                D[d] = sc.nextInt();
            }
            int[] inDegree = new int[N]; // 사전 건물 연결 갯수
            Map<Integer, List<Integer>> roleMap = new HashMap<>(); // key: 건물 번호, value: 다음 건설 건물
            for (int k = 0; k < K; k++) {
                int x = sc.nextInt() - 1;
                int y = sc.nextInt() - 1;
                roleMap.computeIfAbsent(x, key -> new ArrayList<>()).add(y);
                inDegree[y]++;
            }
            int W = sc.nextInt() - 1;
            getBuildingTime(W, N, D, inDegree, roleMap);
        }
    }
    private static void getBuildingTime(int W, int N, int[] D, int[] inDegree, Map<Integer, List<Integer>> roleMap) {
        int[] buildTime = new int[N];
        Queue<Integer> queue = new LinkedList<>();
        // 시작점인 경우
        for (int i = 0; i < N; i++) {
            if (inDegree[i] == 0) {
                buildTime[i] = D[i];
                queue.add(i);
            }
        }
        while (!queue.isEmpty()) {
            int buildingNo = queue.poll();
            if (roleMap.containsKey(buildingNo)) {
                List<Integer> nextBuildings = roleMap.get(buildingNo);
                for (int nextNo : nextBuildings) {
                    buildTime[nextNo] = Math.max(buildTime[nextNo], buildTime[buildingNo] + D[nextNo]);
                    inDegree[nextNo]--;
                    if (inDegree[nextNo] == 0) {
                        queue.add(nextNo);
                    }
                }
            }
        }
        System.out.println(buildTime[W]);
    }
}
