✔ 난이도 - Silver 1
인접행렬에서 1인 좌표를 graph에 저장(방향0)
(0,0) ~ (N-1,N-1) 까지 돌면서 i->j 길이 있는지 graph를 통해서 확인
이 풀이는 번의 DFS를 돌리고 있는데, 번의 DFS만으로도 해결이 가능하다.
(하단의 개선 풀이 참고)
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int N = Integer.parseInt(br.readLine());
ArrayList<Integer>[] graph = new ArrayList[N];
for (int i = 0; i < N; i++){
graph[i] = new ArrayList<>();
}
StringTokenizer st;
for (int i = 0; i < N; i++){
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++){
int num = Integer.parseInt(st.nextToken());
if (num == 1) {
graph[i].add(j);
}
}
}
// System.out.println(Arrays.toString(graph));
Stack<Integer> stack;
boolean[] visited;
int answer = 0;
for (int i = 0; i < N; i++){
for (int j = 0; j < N; j++){
stack = new Stack<>();
visited = new boolean[N];
answer = 0;
stack.push(i);
// visited[i] = true;
while (!stack.isEmpty()) {
int node = stack.pop();
if (node == j && visited[node] == true){
answer = 1;
break;
}
for (int n : graph[node]) {
if (visited[n] == false) {
stack.push(n);
visited[n] = true;
}
}
}
sb.append(answer).append(" ");
}
sb.append("\n");
}
System.out.println(sb);
}
}

알고리즘 문제에서는 이미 구한 정보를 어떻게 재활용할까(여기서는 한 번의 DFS로 모든 j 확인하기)를 고민하자
출발점 i가 정해지면, 한 번의 DFS만으로도 i에서 갈 수 있는 모든 노드를 다 알아낼 수 있다.
위의 방식처럼 j를 하나하나 바꿔가며 DFS를 새로 시작하지 않아도 됨
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int N = Integer.parseInt(br.readLine());
ArrayList<Integer>[] graph = new ArrayList[N];
for (int i = 0; i < N; i++){
graph[i] = new ArrayList<>();
}
StringTokenizer st;
for (int i = 0; i < N; i++){
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++){
int num = Integer.parseInt(st.nextToken());
if (num == 1) {
graph[i].add(j);
}
}
}
// System.out.println(Arrays.toString(graph));
Stack<Integer> stack = new Stack<>();
boolean[][] visited = new boolean[N][N];;
for (int i = 0; i < N; i++){
stack.clear();
stack.push(i);
while (!stack.isEmpty()){
int node = stack.pop();
for (int n : graph[node]){
if (visited[i][n] == false){
stack.push(n);
visited[i][n] = true;
}
}
}
}
for (int i = 0; i < N; i++){
for (int j = 0; j < N; j++){
sb.append(visited[i][j] ? "1 " : "0 ");
}
sb.append("\n");
}
System.out.println(sb);
}
}

주어진 조건()을 기준으로 두 코드의 시간 복잡도를 계산해 보면, 왜 두 번째 방식이 안전한지 수치로 확인할 수 있다.
그래프에서 간선의 개수 은 최대 (모든 노드가 서로 연결된 경우)이 될 수 있다는 점을 기억하고 진행
| 구분 | 시간 복잡도 | 실제 연산 횟수 (최악의 경우) | 여유도 |
|---|---|---|---|
| 이전 코드 | 100,000,000 | 위험 | |
| 고친 코드 | 1,000,000 | 매우 안전 (100배 빠름) |
인접 행렬 문제에서 간선의 개수 은 최대
이 문제의 정석 풀이 중 하나인 '플로이드-워셜(Floyd-Warshall)' 알고리즘도 시간 복잡도가
"모든 지점 사이의 최단 거리를 구해라" 같은 문제에 대해서는 플로이드-워셜이 좋음