Problem Solving/[ν•­ν•΄99] TIL

99클럽 μ½”ν…Œ μŠ€ν„°λ”” 8일차 TIL / DFS(깊이 μš°μ„  탐색)

geum 2024. 11. 4. 20:11

✏️ 문제

https://www.acmicpc.net/problem/2644

 

우리 λ‚˜λΌλŠ” κ°€μ‘± ν˜Ήμ€ μΉœμ²™λ“€ μ‚¬μ΄μ˜ 관계λ₯Ό μ΄Œμˆ˜λΌλŠ” λ‹¨μœ„λ‘œ ν‘œν˜„ν•˜λŠ” λ…νŠΉν•œ λ¬Έν™”λ₯Ό 가지고 μžˆλ‹€. μ΄λŸ¬ν•œ μ΄Œμˆ˜λŠ” λ‹€μŒκ³Ό 같은 λ°©μ‹μœΌλ‘œ κ³„μ‚°λœλ‹€. 기본적으둜 λΆ€λͺ¨μ™€ μžμ‹ 사이λ₯Ό 1촌으둜 μ •μ˜ν•˜κ³  μ΄λ‘œλΆ€ν„° μ‚¬λžŒλ“€ κ°„μ˜ 촌수λ₯Ό κ³„μ‚°ν•œλ‹€. 예λ₯Ό λ“€λ©΄ λ‚˜μ™€ 아버지, 아버지와 ν• μ•„λ²„μ§€λŠ” 각각 1촌으둜 λ‚˜μ™€ ν• μ•„λ²„μ§€λŠ” 2촌이 되고, 아버지 ν˜•μ œλ“€κ³Ό ν• μ•„λ²„μ§€λŠ” 1촌, λ‚˜μ™€ 아버지 ν˜•μ œλ“€κ³ΌλŠ” 3촌이 λœλ‹€.

μ—¬λŸ¬ μ‚¬λžŒλ“€μ— λŒ€ν•œ λΆ€λͺ¨ μžμ‹λ“€ κ°„μ˜ 관계가 μ£Όμ–΄μ‘Œμ„ λ•Œ, 주어진 두 μ‚¬λžŒμ˜ 촌수λ₯Ό κ³„μ‚°ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ‹œμ˜€.

 

πŸ€– μž…μΆœλ ₯ μ˜ˆμ‹œ

 

🧐 λ‚œμ΄λ„/μ†Œμš” μ‹œκ°„

βœ… λ‚œμ΄λ„: solved.ac κΈ°μ€€ S2

βœ… μ†Œμš” μ‹œκ°„: μ˜ˆμ „μ— ν’€μ—ˆλ˜ 문제라 μΈ‘μ • X

βœ… ꢌμž₯ μ‹œκ°„: 1μ‹œκ°„

 

πŸ’‘ 풀이

1) 문제 μœ ν˜• νŒŒμ•…

'촌수'λΌλŠ” κ°œλ… μžμ²΄κ°€ κ³„μ†ν•΄μ„œ 타고 λ“€μ–΄κ°€λŠ” 거라 보자마자 DFS둜 ν’€μ–΄μ•Όκ² λ‹€λŠ” 생각을 ν–ˆλ‹€.

 

ν•˜μ§€λ§Œ λ‚΄ κ²½ν—˜μƒ 보톡 DFS둜 ν’€ 수 μžˆλŠ” λ¬Έμ œλŠ” BFSλ‘œλ„ ν’€ 수 있고 μ›¬λ§Œν•˜λ©΄ κ·Έ λ°˜λŒ€λ„ κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— BFSλ‘œλ„ ν’€μ–΄λ΄€λ‹€. μ•„λž˜ κ΅¬ν˜„ 뢀뢄에 두 μ½”λ“œλ₯Ό λͺ¨λ‘ μ •λ¦¬ν•΄λ‘μ—ˆλ‹€.

 

2) 아이디어 흐름

β‘  DFS κΈ°λ³Έ ν‹€ ꡬ상

Step. 1: DFS ν•¨μˆ˜ 인자 μ •μ˜

❎ 촌수 기둝용 λ³€μˆ˜ μ„ μ–Έ?

이건 틀렸닀기보닀 ꡳ이 μ΄λ ‡κ²Œ ν•  μ΄μœ κ°€ μ—†λ‹€κ³  μƒκ°λ˜λŠ” 뢀뢄이닀.

 

μ²˜μŒμ—λŠ” 촌수λ₯Ό κΈ°λ‘ν•˜κΈ° μœ„ν•œ μ „μ—­λ³€μˆ˜λ₯Ό λ”°λ‘œ μ„ μ–Έν•΄μ€¬λŠ”λ° DFS ν•¨μˆ˜μ— κ°’λ§Œ 잘 λ„˜κ²¨μ€€λ‹€λ©΄ μ „μ—­λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ λ¬Έμ œκ°€ 해결될 것 κ°™μ•˜λ‹€.

 

βœ… DFS ν•¨μˆ˜μ˜ μž…λ ₯ = μ‹œμž‘ λ…Έλ“œ, 촌수 μ΄ˆκΈ°κ°’

μ „μ—­λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠκΈ° μœ„ν•œ 방법은 촌수 μ΄ˆκΈ°κ°’μ„ DFS ν•¨μˆ˜μ˜ 인자둜 λ„˜κ²¨μ£ΌλŠ” 것이닀.

 

ν˜„μž¬ λ…Έλ“œμ™€ μ—°κ²°λœ λ…Έλ“œλ₯Ό ν•˜λ‚˜μ”© ν™•μΈν•˜λ©΄μ„œ λ°©λ¬Έν•˜μ§€ μ•Šμ€ λ…Έλ“œμΌ 경우 DFS ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ²Œ λ˜λŠ”λ°, 이 λ•Œ ν˜„μž¬ 촌수+1을 ν•¨κ»˜ λ„˜κ²¨μ£Όλ©΄ μž¬κ·€κ°€ κΉŠμ–΄μ§€λ©΄μ„œ μžμ—°μŠ€λŸ½κ²Œ μ΄Œμˆ˜κ°€ μ¦κ°€ν•˜κ²Œ λœλ‹€.

 

Step. 2: μž¬κ·€ μ’…λ£Œ 쑰건 μ„€μ •

βœ… ν˜„μž¬ λ…Έλ“œκ°€ t2λ©΄ ν•¨μˆ˜ μ’…λ£Œ

 

촌수λ₯Ό 계산해야 ν•˜λŠ” μ„œλ‘œ λ‹€λ₯Έ 두 μ‚¬λžŒμ˜ 번호λ₯Ό target1, target2λΌλŠ” μ˜λ―Έμ—μ„œ t1, t2둜 μ§€μ—ˆκΈ° λ•Œλ¬Έμ— 'ν˜„μž¬ λ…Έλ“œκ°€ t2λ©΄ ν•¨μˆ˜ μ’…λ£Œ'라고 μ μ—ˆλ‹€.

 

Step.1μ—μ„œ DFS ν•¨μˆ˜λ₯Ό μ‹œμž‘ λ…Έλ“œμ™€ 촌수 μ΄ˆκΈ°κ°’μ„ 인자둜 λ°›λŠ”λ‹€κ³  ν–ˆλŠ”λ° 이 λ•Œ μ‹œμž‘ λ…Έλ“œλ‘œ t1을 μ‚¬μš©ν–ˆλ‹€. 예제 μž…λ ₯ 1을 μ΄μš©ν•΄ t1μ—μ„œ t2둜 κ°€λŠ” 과정은 μ•„λž˜ ν‘œμ™€ κ°™λ‹€.

 

ν˜„μž¬ 확인 쀑인 λ…Έλ“œκ°€ 3이라면 더 탐색할 ν•„μš” 없이 ν•¨μˆ˜λ₯Ό μ’…λ£Œν•˜λ©΄ λœλ‹€.

 

Step. 3: answer 리턴 κ²°κ³Ό 확인

DFS μ½”λ“œμ—μ„œ μ£Όμ„μœΌλ‘œ '이 λΆ€λΆ„ μ—†μœΌλ©΄ ν‹€λ¦Ό'이라고 λΌμžˆλŠ” 두 쀄을 λ„£λŠ” 게 제일 μ–΄λ €μ› λ‹€. 이 λΆ€λΆ„μ˜ μž‘λ™ 방식 이해λ₯Ό μœ„ν•΄ μ•„λž˜μ²˜λŸΌ print문을 μΆ”κ°€ν•΄ κ²°κ³Όλ₯Ό 찍어봀닀.

 

 

 

이게 κΈ€λ‘œ 적으면 잘 전달될지 λͺ¨λ₯΄κ² μ§€λ§Œ 일단 μ μ–΄λ³΄μžλ©΄..!

 

ν•¨μˆ˜λŠ” DFS(7, 0) → DFS(2, 1) → DFS(3, 3) μˆœμ„œλ‘œ 호좜되고 DFS(3, 3)은 t2 == 3이기 λ•Œλ¬Έμ— ν˜„μž¬κΉŒμ§€ 기둝된 촌수 3을 λ¦¬ν„΄ν•œλ‹€. 그럼 DFS(3, 3)μœΌλ‘œλΆ€ν„° λ¦¬ν„΄λœ 값이 None이 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— answer κ°’ λ˜ν•œ 3이 λœλ‹€.

 

그리고 μ’…λ£Œ 쑰건이 호좜된 μƒνƒœλ‘œλΆ€ν„° λ‹€μ‹œ 이전 ν•¨μˆ˜λ‘œ λŒμ•„μ™€ κ³„μ†ν•΄μ„œ 값을 λ¦¬ν„΄ν•΄μ£ΌλŠ”λ° answer 값은 3으둜 λ³€ν™”κ°€ μ—†κ³  None도 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— 계속 3이 λ¦¬ν„΄λ˜μ–΄ μ΅œμ’… 값이 λœλ‹€. → 이 λΆ€λΆ„ λ­”κ°€ μ„€λͺ…이 μ΄μƒν•΄μ„œ ν˜Ήμ‹œλ‚˜ 이 글을 보고 계신 뢄이 μžˆλ‹€λ©΄ 일단 νŒ¨μŠ€ν•΄μ£Όμ‹œκΈ°λ₯Ό,,⭐

 

β‘‘ BFS κΈ°λ³Έ ν‹€ ꡬ상

BFSλŠ” 일반적으둜 μ‚¬μš©ν•˜λŠ” κ·Έ κ΅¬μ‘°λŒ€λ‘œ κ΅¬ν˜„ν•΄μ£Όλ©΄ λœλ‹€. 이 λ¬Έμ œμ—μ„œ μ‹ κ²½ 썼던 뢀뢄은 ν˜„μž¬ λ…Έλ“œμ™€ 촌수λ₯Ό ν•˜λ‚˜μ˜ νŠœν”Œλ‘œ λ¬Άμ–΄ 큐에 같이 λ„£μ–΄μ€€ 것이닀.

 

3-1) κ΅¬ν˜„: DFS

N = int(input())

t1, t2 = map(int, input().split())

M = int(input())

family = {k: [] for k in range(1, N+1)}

visited = [False]*(N+1)

for _ in range(M):
    x, y = map(int, input().split())    # xλŠ” y의 λΆ€λͺ¨
    
    family[x].append(y)
    family[y].append(x)

def DFS(node, score):    
    visited[node] = True
    
    if node == t2:
        return score
    
    for child in family[node]:
        if not visited[child]:
            answer = DFS(child, score+1)
            
            # 이 λΆ€λΆ„ μ—†μœΌλ©΄ ν‹€λ¦Ό
            if answer is not None:
                return answer

answer = DFS(t1, 0)

if answer is None:
    print(-1)
else:
    print(answer)

 

3-2) κ΅¬ν˜„: BFS

N = int(input())

t1, t2 = map(int, input().split())

M = int(input())

family = {k: [] for k in range(1, N+1)}

visited = [False]*(N+1)

for _ in range(M):
    x, y = map(int, input().split())    # xλŠ” y의 λΆ€λͺ¨
    
    family[x].append(y)
    family[y].append(x)

def BFS(node, depth):
    from collections import deque
    
    queue = deque([(node, depth)])
    
    visited[node] = True
    
    while queue:
        current_node, current_depth = queue.popleft()

        if current_node == t2:
            return current_depth
        
        for member in family[current_node]:
            if not visited[member]:
                visited[member] = True
                queue.append((member, current_depth+1))
            
    return None

answer = BFS(t1, 0)

print(answer)

 

μ˜ˆμ „μ— λ‹€λ₯Έ 문제 ν’€μ΄μ—μ„œ 'BFSκ°€ DFS보닀 λΉ λ₯΄λ‹€κ³  ν•˜μ§€λ§Œ λ‚˜λŠ” μ•„μ§κΉŒμ§€ BFSκ°€ 더 빨랐던 문제λ₯Ό λ³Έ 적이 μ—†λ‹€'κ³  μ“΄ 적이 μžˆλ‹€.

 

이 λ¬Έμ œμ—μ„œλ„ DFS의 μ‹€ν–‰ μ‹œκ°„μ€ 32ms, BFS의 μ‹€ν–‰ μ‹œκ°„μ€ 52ms둜 DFSκ°€ 더 λΉ λ₯Έ 것을 확인할 수 μžˆμ—ˆλ‹€. μ΄μœ κ°€ 뭐지?

 

4) μ–΄λ €μ› λ˜ 점/배운 μ 

🚨 μ–΄λ €μ› λ˜ 점

  • DFSλŠ” μž¬κ·€λ₯Ό μ“°μ§€λ§Œ λ‚΄κ°€ 문제λ₯Ό ν’€ λ•Œ μž¬κ·€ κ°œλ…μ„ μ™„λ²½ν•˜κ²Œ μ΄ν•΄ν–ˆλ‹€κΈ°λ³΄λ‹€ μ½”λ“œλ₯Ό ν•˜λ‚˜μ˜ ν…œν”Œλ¦Ώμ²˜λŸΌ μ™Έμ›Œλ‘κ³  μ‚¬μš©ν•΄μ„œ κ·ΈλŸ°κ°€ μ–΄λŠ μ‹œμ μ—μ„œ κ²°κ³Ό 값을 return ν•΄μ•Ό ν•˜λŠ”μ§€ μ •ν•˜λŠ” 게 항상 μ–΄λ ΅λ‹€.
  • 이 뢀뢄은 μž¬κ·€ 곡뢀λ₯Ό μΆ”κ°€μ μœΌλ‘œ ν•΄μ•Όλ§Œ 해결될 것이기 λ•Œλ¬Έμ— μ‹œκ°„ λ‚΄μ„œ μž¬κ·€μ— λŒ€ν•΄ λ‹€μ‹œ 봐야겠닀.

❗ 배운 점

  • DFS 문제λ₯Ό μ–‘μΉ˜κΈ° ν•˜λŠ” 것도 μ’‹μ§€λ§Œ λ‚œ κ·Έ 전에 μž¬κ·€ κ°œλ…μ„ μ™„λ²½ν•˜κ²Œ μ΄ν•΄ν•˜λŠ” 것이 더 쒋을 것 κ°™λ‹€ ^^..