https://www.acmicpc.net/problem/1005
4번을 위해선, 2번 3번이 완료되어야 한다.
2번,3번은 “동시 진행이 가능" 하다 → 결국 나중에 완료되는 건물에 걸리는 시간이 측정된다
4 3
5 5 5 5
1 2
1 3
2 3
4
위상정렬
- 그래프의 어떤 노드를 방문하기 위해서는, 반드시 선행 노드들이 이미 모두 방문되어야 하는 것.
- 하나의 조건이 만족되면
- 이를 조건으로 하던 모든 노드 의 진입차수를 1씩 감소시킨다.
- 그 결과 진입차수가 0이 된 노드 들을 “Priority Queue” 에 넣는다.
- 그럼 현재의 큐 에는 “현재 건설 가능한 건물들" 이 들어가 있다. → 여기서 “가장 오랜 시간이 걸리는 애"의 시간만을 사용한다.
뒤집어서 풀어야겠다고 생각했던 이유는, “시작점"을 모르겠어서…라고 생각했는데
위에서부터 차례로 진입차수가 0인 노드들을 먼저 넣고 빼는 식으로 해도 되긴 할 듯 하다.
이렇게 문제를 풀기 위해서는 어떻게 해야할까?
전체 노드는 1000 이하
만약, 각 노드의 부모가 1000개, 자식이 1000개 더라도, 2000 x 1000 → 200만정도?
풀리긴 할 것 같음.
1
4 4
10 1 100 10
1 2
1 3
2 4
3 4
4
1
8 8
10 20 1 5 8 7 1 43
1 2
1 3
2 4
2 5
3 6
5 7
6 7
7 8
7
package learningJava.algorithm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.StringTokenizer;
public class Main {
private BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private int[] times; // 각 건물 짓는데 걸리는 시간
private int[] minTime; // 각 건물 짓는데 걸리는 최소시간
private int[] adjNumb; // 진입차수
private boolean[] visit;
private List<List<Integer>> childG = new LinkedList<>(new LinkedList<>());
private List<List<Integer>> parentG = new LinkedList<>(new LinkedList<>());
private StringTokenizer st;
public void run() throws IOException {
int t = Integer.parseInt(br.readLine());
int n = 0, k = 0, w = 0;
for (int i = 0; i < t; i++) {
st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken()); // 건물의 개수
k = Integer.parseInt(st.nextToken()); // 규칙 개수
// 초기화
times = new int[n + 1];
adjNumb = new int[n + 1];
visit = new boolean[n + 1];
minTime = new int[n + 1];
childG.clear();
parentG.clear();
// 각 노드에 대한 List 를 추가
for (int j = 0; j < n + 1; j++) {
childG.add(new LinkedList<>());
parentG.add(new LinkedList<>());
}
// 건설 시간
st = new StringTokenizer(br.readLine());
for (int j = 1; j < n + 1; j++) {
times[j] = Integer.parseInt(st.nextToken());
}
// 건설 순서
for (int o = 0; o < k; o++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken()); // x 는 y 의 전제 조건 -> y 는 x 의 하위노드
int y = Integer.parseInt(st.nextToken());
childG.get(x).add(y);
adjNumb[y]++;
parentG.get(y).add(x);
}
// 목표 건물
w = Integer.parseInt(br.readLine());
System.out.println(solve(w));
}
}
public int solve(int target) {
Optional<Integer> next;
while ((next = nextNode()).isPresent()) {
int cur = next.get();
// 선행 노드들에 대한 시간들 중 max
int maxParent = parentG.get(cur).stream()
.map(node -> minTime[node])
.max(Comparator.comparing(x -> x))
.orElse(0);
minTime[cur] = times[cur] + maxParent;
// 진입 차수 감소시키기
for (Integer child : childG.get(cur)) {
// 자식 노드의 진입차수를 1 감소시킨다
adjNumb[child]--;
}
}
return minTime[target];
}
// 진입 차수가 0 인 노드 찾기 - 없으면 empty optional
private Optional<Integer> nextNode() {
for (int idx = 1; idx < adjNumb.length; idx++) {
if (visit[idx] || adjNumb[idx] != 0) {
continue;
}
visit[idx] = true;
return Optional.of(idx);
}
return Optional.ofNullable(null);
}
public static void main(String[] args) throws IOException {
Main main = new Main();
main.run();
}
}
Optional 을 parameter 에 사용하지 않는 게 좋은 이유는 아래와 같은 글들을 본적이 있다.
그리고 isPresent(), get() 을 사용하는 것 역시 좋지 않은 예시라고 들었다.
그런데 isPresent(), get() 을 사용하는 중이라 매우 찝찝하다..
Java static code analysis: "Optional" should not be used for parameters
자바 언어에서 Optional 은 리턴타입으로 사용되는 것을 목적으로 만들어졌다. 해당 메소드가 값이 아닌 것(null) 을 반환할 수도 있음을 나타내는 용도가 주된 용도라고 볼 수 있다.
Optional 을 parameter 로 사용하게 되면, 기존에는
이 두가지 경우의 parameter 가 오는 것만을 예상하고 이에 대한 처리를 해 주면 되었었는데,
이제는 세 가지 경우가 나오게 되는 것임