새소식

PS/프로그래머스

[프로그래머스] 등대 (Python)

  • -

Problem : https://school.programmers.co.kr/learn/courses/30/lessons/133500

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

Status : Solved

Time : 00:16:28

 


 

문제 설명

 

더보기

인천 앞바다에는 1부터 n까지 서로 다른 번호가 매겨진 등대 n개가 존재합니다. 등대와 등대 사이를 오가는 뱃길이 n-1개 존재하여, 어느 등대에서 출발해도 다른 모든 등대까지 이동할 수 있습니다. 등대 관리자 윤성이는 전력을 아끼기 위하여, 이 중 몇 개의 등대만 켜 두려고 합니다. 하지만 등대를 아무렇게나 꺼버리면, 뱃길을 오가는 배들이 위험할 수 있습니다. 한 뱃길의 양쪽 끝 등대 중 적어도 하나는 켜져 있도록 등대를 켜 두어야 합니다.

예를 들어, 아래 그림과 같이 등대 8개와 7개의 뱃길들이 있다고 합시다. 이 경우 1번 등대와 5번 등대 두 개만 켜 두어도 모든 뱃길은 양쪽 끝 등대 중 하나가 켜져 있으므로, 배들은 안전하게 운항할 수 있습니다.

 

등대의 개수 n과 각 뱃길이 연결된 등대의 번호를 담은 이차원 배열 lighthouse가 매개변수로 주어집니다. 윤성이가 켜 두어야 하는 등대 개수의 최솟값을 return 하도록 solution 함수를 작성해주세요.

 

입력 및 출력

 

더보기

제한사항

  • 2 ≤ n ≤ 100,000
  • lighthouse의 길이 = n – 1
    • lighthouse 배열의 각 행 [a, b]는 a번 등대와 b번 등대가 뱃길로 연결되어 있다는 의미입니다.
      • 1 ≤ a ≠ b ≤ n
      • 모든 등대는 서로 다른 등대로 이동할 수 있는 뱃길이 존재하도록 입력이 주어집니다.

 

입출력

n lighthouse result
8 [[1, 2], [1, 3], [1, 4], [1, 5], [5, 6], [5, 7], [5, 8]] 2
10 [[4, 1], [5, 1], [5, 6], [7, 6], [1, 2], [1, 3], [6, 8], [2, 9], [9, 10]] 3

 

 


 

풀이

트리 dp 문제의 교과서적인 문제라고 생각한다. n개의 등대, 그리고 등대 사이의 n-1개의 경로 및 등대 간의 통행 보장은 등대가 트리 자료구조를 이루고 있음을 암시한다. 따라서, 트리 탐색을 통해 계층적으로 문제를 풀어나갈 수 있다. 또한 트리의 각 서브트리의 해가 전체 트리의 해를 보장하므로 쪼갤 수 있는 문제애 속한다(각 서브트리의 등대의 불빛 최소값에 따라 전체 등대의 불빛 최솟값이 결정된다). 따라서 dp를 사용할 수 있다.

 

어떤 서브트리의 루트 노드 등대에 대해 생각해보자. 루트 노드 등대의 경우의 수는 총 두가지이다(켜거나, 끄거나). 

첫번째로, 루트 노드 등대를 켜는 경우는 자식 노드가 켜져 있던, 꺼져 있던 제약을 받지 않게 된다. 따라서 (각 자식 노드의 on/off 경우의 수의 최솟값)의 합.+ 1 (루트 노드 등대 역시 켜지므로)로 전체 최솟값이 결정된다.

두 번째로, 루트 노드 등대를 끄는 경우를 생각해보자. 이 경우는 한 뱃길의 양쪽 끝 등대 중 최소 한 등대는 켜져 있어야 한다는 전제조건에 따라, 반대쪽 끝인 자식 노드들은 모두 켜져 있어야 한다. 따라서 (각 자식 노드의 on 경우의 수)의 합이 된다.

이런 식으로 재귀적으로 dp 배열을 채워넣은 뒤, 최종적으로 전체 루트 노드의 dp값의 최솟값을 출력하면 해결된다.

 

풀이 코드

import sys
sys.setrecursionlimit(100000)

def solution(n, lighthouse):
    dp = [[0]*2 for _ in range(n)]
    node_dict = { key : [] for key in range(n) }
    visited = [False]*n
    for a, b in lighthouse :
        node_dict[a-1].append(b-1)
        node_dict[b-1].append(a-1)
        
    def dfs(node) :
        visited[node] = True
        leaf_list = list()
        for leaf in node_dict[node] :
            if visited[leaf] :
                continue
            leaf_list.append(leaf)
            dfs(leaf)

        dp[node][0] = 0
        dp[node][1] = 1

        for leaf in leaf_list :
            dp[node][1] += min(dp[leaf])
            dp[node][0] += dp[leaf][1]
        return
    
    dfs(0)
        
    return min(dp[0])

풀이 완료!

Contents

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

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