-
Notifications
You must be signed in to change notification settings - Fork 1
/
p2150.java
242 lines (200 loc) · 7.77 KB
/
p2150.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
SCC : Strongly Conneceted Component
SCC를 구하기 위해
1. 코사라주 알고리즘 과
2. 타잔 알고리즘 을 이용한다.
*/
import java.io.*;
import java.util.*;
public class p2150 {
static List<List<Integer>> graph;
static List<List<Integer>> rgraph;
static List<List<Integer>> res;
static boolean[] visited;
static Stack<Integer> stack;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int V = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
graph = new ArrayList<>();
rgraph = new ArrayList<>();
res = new ArrayList<>();
for (int i = 0; i <= V; i++) {
graph.add(new ArrayList<>());
rgraph.add(new ArrayList<>());
res.add(new ArrayList<>());
}
// 단방향 인접리스트 구현 (방향 , 역방향 그래프)
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
graph.get(u).add(v);
rgraph.get(v).add(u);
}
visited = new boolean[V + 1];
stack = new Stack<>();
// 방향 그래프에 대해 dfs 수행
for (int i = 1; i <= V; i++) {
if (!visited[i]) {
dfs(i);
}
}
Arrays.fill(visited, false);
int groupNum = 0;
// 역방향 그래프에 대해 dfs 수행
while (!stack.isEmpty()) {
int start = stack.pop();
// 스택에서 하나씩 꺼내면서!
// 이 때 방문 체크가 된 것은 start가 하나의 SCC그룹에 속해있다는 뜻!
if (visited[start]) {
continue;
}
// 같은 그룹끼리(groupNum으로 분류) SCC를 분류한다.
redfs(start, groupNum);
groupNum++;
}
StringBuilder sb = new StringBuilder();
// SCC 그룹 개수
sb.append(groupNum + "\n");
// key를 기준으로 오름차순 정렬하기 위한 TreeMap 선언
TreeMap<Integer, Integer> tm = new TreeMap<>();
for (int i = 0; i < groupNum; i++) {
// 각각의 SCC 그룹에 대해 오름차순 정렬한다.
Collections.sort(res.get(i));
// key : SCC그룹의 첫째 항
// value : index
tm.put(res.get(i).get(0), i);
}
// map의 value를 이용하여 첫번째 항이 작은 순서대로 출력 (오름차순)
tm.keySet().forEach(key -> {
int value = tm.get(key);
for (int now : res.get(value)) {
sb.append(now + " ");
}
sb.append("-1\n"); // 문제조건에 따라 끝에 -1 붙이기
});
bw.write(sb.toString());
bw.flush();
bw.close();
}
// 끝나는 점에 대해서 stack에 push
static void dfs(int start) {
visited[start] = true;
for (int cur : graph.get(start)) {
if (!visited[cur]) {
dfs(cur);
}
}
stack.push(start);
}
// 같은 SCC 그룹은 groupNum으로 분류한다.
// 결과값을 담는 res 코드가 추가된다.
static void redfs(int start, int groupNum) {
visited[start] = true;
res.get(groupNum).add(start);
for (int cur : rgraph.get(start)) {
if (!visited[cur]) {
redfs(cur, groupNum);
}
}
}
}
/* 타잔 알고리즘
import java.io.*;
import java.util.*;
public class p2150_ver2 {
static List<List<Integer>> graph;
static List<List<Integer>> res;
static int cnt = 0, groupNum = 0;
static int[] dfsn, sn;
static boolean[] finished; // SCC가 확정된 정점 판별
static Stack<Integer> stack;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
int V = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
dfsn = new int[V + 1];
sn = new int[V + 1];
graph = new ArrayList<>();
res = new ArrayList<>();
for (int i = 0; i <= V; i++) {
graph.add(new ArrayList<>());
res.add(new ArrayList<>());
}
// 단방향 인접리스트 구현 (방향 , 역방향 그래프)
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
graph.get(u).add(v);
}
finished = new boolean[V + 1];
stack = new Stack<>();
// 방향 그래프에 대해 dfs 수행
for (int i = 1; i <= V; i++) {
if (dfsn[i] == 0) {
dfs(i);
}
}
StringBuilder sb = new StringBuilder();
// SCC 그룹 개수
sb.append(groupNum + "\n");
// key를 기준으로 오름차순 정렬하기 위한 TreeMap 선언
TreeMap<Integer, Integer> tm = new TreeMap<>();
for (int i = 0; i < groupNum; i++) {
// 각각의 SCC 그룹에 대해 오름차순 정렬한다.
Collections.sort(res.get(i));
// key : SCC그룹의 첫째 항
// value : index
tm.put(res.get(i).get(0), i);
}
// map의 value를 이용하여 첫번째 항이 작은 순서대로 출력 (오름차순)
tm.keySet().forEach(key -> {
int value = tm.get(key);
for (int now : res.get(value)) {
sb.append(now + " ");
}
sb.append("-1\n"); // 문제조건에 따라 끝에 -1 붙이기
});
bw.write(sb.toString());
bw.flush();
bw.close();
}
// 각 정점에 대해 dfs 수행
static int dfs(int start) {
dfsn[start] = ++cnt; // 노드 마다 고유한 SCC 번호를 할당한다.
stack.push(start); // 스택에 자기 자신을 삽입
// 자신의 dfs, 자식들의 결과나 dfsn 중 가장 작은 번호를 result에 저장
int parent = dfsn[start];
for (int next : graph.get(start)) {
// 아직 방문하지 않은 이웃에 대하여
if (dfsn[next] == 0) {
parent = Math.min(parent, dfs(next));
}
// 방문은 했으나, 아직 SCC로 추출되지 않은 이웃
else if (!finished[next]) {
parent = Math.min(parent, dfsn[next]);
}
}
// 부모노드가 자기 자신일 경우
// 자신과 자신의 자손들이 도달 가능한 제일 높은 정점이 자신일 경우 SCC 추출
if (parent == dfsn[start]) {
while (true) {
int t = stack.pop();
finished[t] = true;
sn[t] = groupNum;
res.get(groupNum).add(t);
if (t == start) break;
}
groupNum++;
}
// 자신의 부모값을 반환
return parent;
}
}
*/