새소식

Computer Science/코딩테스트 문제 풀이

⭐⭐⭐⭐[코드트리] 그래프와 트리

  • -

https://www.codetree.ai/missions/9/problems/graphs-and-trees?&utm_source=clipboard&utm_medium=text

 

코드트리 | 코딩테스트 준비를 위한 알고리즘 정석

국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.

www.codetree.ai

 

입력으로 그래프가 주어집니다. 이 그래프는 연결 그래프가 아닐 수 있습니다. 다시 말해서, 여러 개의 연결 요소로 이루어져 있을 수 있습니다.

여기서 연결 요소는

(1) 모든 정점이 서로 연결되어 있는 정점의 부분집합이며

(2) 이 연결 요소에 포함되는 정점들은 외부 정점들과 연결되어서는 안됩니다.

트리는 다음 조건을 만족하는 연결 요소입니다. 셋 중 하나만 만족해도 나머지를 만족하게 됩니다.

  • 사이클이 없습니다.
  • 정점이 n개이면, 간선이 n - 1개 있습니다
  • 임의의 두 정점에 대해서 경로가 유일합니다.

이 그래프상의 연결 요소 중, 트리에 해당하는 것의 개수를 세는 프로그램을 작성해보세요.

 

--> 즉 사이클이 없는 노드의 연결들을 하나로 카운트하는 트리의 개수 구하기 문제 

입력 형식

첫 번째 줄에는 정점의 개수 n, 간선의 개수 m이 주어집니다.

두 번째 줄부터 m+1 번째 줄에 각각 걸쳐 간선으로 연결된 두 개의 노드가 공백을 두고 차례대로 주어집니다. 주어지는 노드의 번호는 1에서 n 사이입니다.

  • 2 ≤ n ≤ 500
  • 1 ≤ m ≤ n(n-1)/2

 

출력형식

주어진 그래프의 연결 요소 중 트리인 것의 개수를 출력합니다.

 

예제1 

 

입력: 

6 3
1 2
2 3
3 4

 

출력:

3

 

-> 설명 

노드가 6번까지 있음, 1-2-3-4로 하나 연결 되어 있고, 5, 6 이렇게 3개가 Tree

 

예제2

입력:

6 5
1 2
2 3
3 4
4 5
5 6

 

출력:

1

 

예제3

입력:

6 6
1 2
2 3
1 3
4 5
5 6
6 4

 

출력:

0

 


BFS, DFS, 유니온 파인드, 그래프, 트리 문제이다. 

 

1. BFS로 푸는 법 

import sys

from collections import deque

def BFS(x):
    isTree = True
    Q=deque()
    Q.append(x)
    
    while Q:
        now = Q.popleft()
        if visited[now]==1:     # 연결된 노드를 타고 가다 이미 방문한적 있는 노드에 도달하면 
            isTree=False        # 그건 사이클이라는 뜻 -> 트리 아님
            
        visited[now]=1
        for e in graph[now]:
            if visited[e]==0:
                Q.append(e)
    
    return isTree

if __name__=="__main__":
    n,m=map(int, input().split())
    graph = [[] for _ in range(n+1)]
    
    for _ in range(m):
        a,b=map(int, input().split())
        graph[a].append(b)
        graph[b].append(a)
        
    visited = [0]*(n+1)
    
    treeCnt=0
    for i in range(1,n+1):
        if visited[i]==1:
            continue
        
        if BFS(i) is True:
            treeCnt += 1
            
    print(treeCnt)

 

2. DFS로 푸는법 

import sys
sys.stdin=open('input.txt', 'r')

# dfs 탐색을 진행합니다.
def DFS(x):
    global vcnt, ecnt
    
    for y in graph[x]:
        # 간선 개수를 세어줍니다.
        ecnt += 1

        # 이미 방문한 정점이면 스킵합니다.
        if visited[y]: 
            continue

        visited[y] = True
        vcnt += 1 # 정점 개수를 세어줍니다.
        DFS(y)


if __name__=="__main__":
    n,m=map(int, input().split())
    graph = [[] for _ in range(n+1)]
    visited = [False]*(n+1)
    for _ in range(m):
        a,b=map(int, input().split())
        graph[a].append(b)
        graph[b].append(a)

    ans = 0
    vcnt = 0
    ecnt = 0
    
    # 모든 정점을 방문해 트리 여부를 탐색합니다.
    for i in range(1, n + 1):
        if visited[i]: 
            continue
        
        visited[i] = True
        vcnt = 1
        ecnt = 0
        DFS(i)

        # 2번씩 중복 세어진 경우를 처리합니다.
        ecnt = ecnt // 2
        
        # 트리의 성질을 만족한다면
        # 답을 갱신해줍니다.
        if vcnt - 1 == ecnt:
            ans += 1

    # 컴퍼넌트 중 트리인 것의 개수를 출력합니다.
    print(ans)

각 연결 요소에 대해 DFS 탐색을 진행하여 정점의 개수 -1이 간선의 개수가 되는지 확인합니다.

 

여러 개의 연결 요소로 이루어진 문제이므로 각 연결 요소에 대해 DFS 탐색을 진행합니다. 탐색 이후의 결과가

'정점의 개수 - 1 = 간선의 개수'를 만족한다면 이는 트리가 됩니다. 따라서 해당 조건을 만족하는 경우를 전부 세어주면 됩니다. n개의 노드와 m개의 간선으로 이루어진 그래프가 주어지므로 총 시간복잡도는 O(N+M)이 됩니다. 

 

3. 유니온 파인드로 푸는 방법 

import sys

def union(x,y):
    x = find(x)
    y = find(y)
    if x < y:
        parents[y] = x
    else:
        parents[x] = y
    
def find(x):
    if x != parents[x]:
        parents[x] = find(parents[x])
    return parents[x]


if __name__=="__main__":
    n,m=map(int, input().split())
    parents = [i for i in range(n+1)]
    cycle = set()
    
    for _ in range(m):
        a,b = map(int,input().split())
        if find(a) == find(b):
            cycle.add(parents[a])
            cycle.add(parents[b])
        # 두 정점 중 하나가 사이클에 포함되는 정점이라면 나머지 정점도 사이클로 포함한다.
        if parents[a] in cycle or parents[b] in cycle: 
            cycle.add(parents[a])
            cycle.add(parents[b])
        union(a,b)
        
    for i in range(n+1):
        find(i)
    
    parents = set(parents)
    ans = sum([1 if i not in cycle else 0 for i in parents])-1
    
    print(ans)
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.