도넛 모양 그래프, 막대 모양 그래프, 8자 모양 그래프들이 있습니다. 이 그래프들은 1개 이상의 정점과, 정점들을 연결하는 단방향 간선으로 이루어져 있습니다.
n인 도넛 모양 그래프는 n개의 정점과 n개의 간선이 있습니다. 도넛 모양 그래프의 아무 한 정점에서 출발해 이용한 적 없는 간선을 계속 따라가면 나머지 n-1개의 정점들을 한 번씩 방문한 뒤 원래 출발했던 정점으로 돌아오게 됩니다. 도넛 모양 그래프의 형태는 다음과 같습니다.
n인 막대 모양 그래프는 n개의 정점과 n-1개의 간선이 있습니다. 막대 모양 그래프는 임의의 한 정점에서 출발해 간선을 계속 따라가면 나머지 n-1개의 정점을 한 번씩 방문하게 되는 정점이 단 하나 존재합니다. 막대 모양 그래프의 형태는 다음과 같습니다.
n인 8자 모양 그래프는 2n+1개의 정점과 2n+2개의 간선이 있습니다. 8자 모양 그래프는 크기가 동일한 2개의 도넛 모양 그래프에서 정점을 하나씩 골라 결합시킨 형태의 그래프입니다. 8자 모양 그래프의 형태는 다음과 같습니다.
도넛 모양 그래프, 막대 모양 그래프, 8자 모양 그래프가 여러 개 있습니다. 이 그래프들과 무관한 정점을 하나 생성한 뒤, 각 도넛 모양 그래프, 막대 모양 그래프, 8자 모양 그래프의 임의의 정점 하나로 향하는 간선들을 연결했습니다.
그 후 각 정점에 서로 다른 번호를 매겼습니다.
이때 당신은 그래프의 간선 정보가 주어지면 생성한 정점의 번호와 정점을 생성하기 전 도넛 모양 그래프의 수, 막대 모양 그래프의 수, 8자 모양 그래프의 수를 구해야 합니다.
그래프의 간선 정보를 담은 2차원 정수 배열 edges가 매개변수로 주어집니다. 이때, 생성한 정점의 번호, 도넛 모양 그래프의 수, 막대 모양 그래프의 수, 8자 모양 그래프의 수를 순서대로 1차원 정수 배열에 담아 return 하도록 solution 함수를 완성해 주세요.
edges의 길이 ≤ 1,000,000
edges의 원소는 [a,b] 형태이며, a번 정점에서 b번 정점으로 향하는 간선이 있다는 것을 나타냅니다.a, b ≤ 1,000,000| edges | result |
|---|---|
| [[2, 3], [4, 3], [1, 1], [2, 1]] | [2, 1, 1, 0] |
| [[4, 11], [1, 12], [8, 3], [12, 7], [4, 2], [7, 11], [4, 8], [9, 6], [10, 11], [6, 10], [3, 5], [11, 1], [5, 3], [11, 9], [3, 8]] | [4, 0, 1, 2] |
입출력 예 #1
주어진 그래프를 그림으로 나타내면 다음과 같습니다.

2번 정점이 생성한 정점이고 도넛 모양 그래프 1개, 막대 모양 그래프 1개가 존재합니다. 따라서 [2, 1, 1, 0]을 return 해야 합니다.
입출력 예 #2
주어진 그래프를 그림으로 나타내면 다음과 같습니다.

4번 정점이 생성한 정점이고 막대 모양 그래프 1개, 8자 모양 그래프 2개가 존재합니다. 따라서 [4, 0, 1, 2]를 return 해야 합니다.
import java.util.*;
class Solution {
public int[] solution(int[][] edges) {
Map<Integer, int[]> node = new HashMap<>();
int[] answer = {0, 0, 0, 0};
// 노드별로 int[] 배열을 생성해서 넣어줌
for(int[] edge : edges) {
int outNode = edge[0];
int inNode = edge[1];
if(!node.containsKey(outNode)) {
node.put(outNode, new int[] {0, 0});
}
if(!node.containsKey(inNode)) {
node.put(inNode, new int[] {0, 0});
}
// node별로 들어오는 개수와 나가는 개수를 계산
node.get(outNode)[0]++;
node.get(inNode)[1]++;
}
// 모든 노드를 탐색
for(int key : node.keySet()) {
int[] count = node.get(key);
// 나가는 간선이 2개 이상이고, 들어오는 간선이 없을 경우
// = 생성한 정점
if(count[0] >= 2 && count[1] == 0) {
answer[0] = key;
}
// 나가는 간선이 없고, 들어오는 간선이 있을 경우
// = 막대 그래프
else if(count[0] == 0 && count[1] > 0) {
answer[2]++;
}
// 들어오는 것과 나가는 것이 각 2개 이상일 경우
// = 8자 그래프
else if(count[0] >= 2 && count[1] >= 2) {
answer[3]++;
}
}
// 정점에서 나가는 간선의 개수에서 막대와 8자를 제외한 경우
// = 도넛 그래프
answer[1] = node.get(answer[0])[0] - answer[2] - answer[3];
return answer;
}
}
HashMap과 그래프를 사용하여 진행하였다.
HashMap은 노드를 나타내며 key값은 노드의 번호를 value값은 int형 배열을 사용하여 나가는 간선의 개수와 들어오는 간선의 개수를 넣어준다.
answer 배열은 생성된 정점, 도넛 그래프 개수, 막대 그래프 개수, 8자 그래프 개수의 순서대로 저장을 해준다.
반복문을 진행하며 HashMap에 노드를 넣어준다. 노드가 존재하지 않는다면 노드를 생성한 뒤에 값을 생성해준다. 이후 노드별로 들어오는 간선의 개수와 나가는 간선의 개수를 증가시켜준다.
모든 노드가 HashMap에 저장이 되었다면, 다시 반복문을 사용하여 모든 노드를 탐색한다.
나가는 간선이 2개 이상이고, 들어오는 간선이 없을 경우는 새로 생성된 정점을 뜻한다. 이유는 새로 정점을 생성한 뒤 모든 그래프로 향하는 간선들을 연결했기 때문이다. 따라서 나가는 간선은 많지만 들어오는 간선이 없는 노드는 새로 생성된 노드이다. 때문에 해당 조건에 만족한다면 answer[0]에 값을 저장해준다.
나가는 간선이 없고, 들어오는 간선이 있을 경우는 막대 그래프의 개수를 뜻한다. 이유는 막대 그래프의 마지막 부분들은 결국 들어오기만 하고 나가는 간선은 없기 때문이다. 따라서 막대 그래프의 개수를 구하기 위해서는 나가는 간선은 없고 들어오는 간선만 있는 노드의 개수를 구하면 된다. 때문에 해당 조건에 만족한다면 answer[2]를 증가시켜준다.
들어오는 것과 나가는 것이 각각 2개 이상일 경우에는 8자 그래프의 개수를 뜻한다. 이유는 8자 그래프가 생성되기 위해서는 결국 들어오는 간선과 나가는 간선이 각각 2개씩 있어야하기 때문이다. 따라서 8자 그래프의 개수를 구하기 위해서는 나가는 간선과 들어오는 간선의 개수가 각각 2개 이상인 노드의 개수를 구하면 된다. 때문에 해당 조건에 만족한다면 answer[3]을 증가시켜준다.
새로운 정점에서 모든 그래프들에 임의의 간선을 연결했다. 따라서 새로운 정점에서 뻗어나간 간선의 개수에서 우리가 구한 막대 그래프의 개수와 8자 그래프의 개수를 빼주면 남은 그래프의 수가 나오고 이는 도넛 그래프가 된다. 때문에 해당 값을 answer[1]에 저장해준다.
위의 반복문이 종료된 뒤에 answer 배열을 return 해주면 문제를 해결할 수 있다!
dfs, bfs를 사용해서 풀수도 있는 문제였지만 그래프의 특징을 사용해서 풀다보니 조금 더 직관적이고 쉽게 풀 수 있었다. 문제를 풀 때 사용하는 풀이 방식이 정해져있다보니 해당 방법으로만 풀려고 해서 너무 어렵게 접근했던 것 같다. 조금 더 다른 관점에서 생각하는 연습을 해볼 필요가 있겠다는 생각을 하게 되었다..
문제에서 answer[0]에 들어갈 정점은 들어오는 간선이 무조건 없나요??