Codeforces Round #635 (Div. 2) #C 문제
오늘은 4/15 어제밤에 치뤘던 대회의 greedy algorithm
문제에 대해 소개드리겠습니다.
문제
- 왕국에는
n
개의 도시와n-1
개의 양방향 도로가 있다. - 어느 도시에서든, 다른 도시로 가는 길이 존재한다.
- 도시는
1
부터n
까지 번호가 매겨져 있고, 도시1
은 왕국의 수도이다. => (트리 구조) - Linova는 정확히
k
개의 도시를 선택하여, 산업도시로 만들것이다. - 1년에 한번 수도에서 회의가 열린다. 회의에 참석하기 위해 각 산업도시에서 사절을 보낸다. 모든 사절들은 가장 짧은 경로로 수도로 향한다.
- 수도로 가는 길에, 각 사절은 그가 가는 길의 관광도시 수 만큼 행복함을 느낀다.
- Linova는 모든 사절들의 행복함의 합이 최대가 되도록
k
개의 도시를 선택하고 싶어한다.
입력
- 첫 번째 줄에 두 개의 정수
n
(도시의 수)과k
(산업도시 수) $(2≤n≤2⋅10^5, 1≤k<n)$ 가 주어진다. - 다음 n-1개의 줄에 각각 두 개의 정수
u
와v
$(1≤u,v≤n)$ 가 주어지며, 이는 도시u
와도시v
를 연결하는 도로가 있음을 나타낸다.
출력
- 모든 사절들의 행복함의 최대 합계를 출력한다.
풀이
우선, 수도(루트 노드)로 가는 사절들의 최단 경로의 길이는, 해당 산업도시의 레벨의 값과 같습니다.
다음의 예제를 트리로 그려봅시다.
이 상태에서, 각 노드의 레벨은 DFS
로 구현할 수 있습니다.
그 코드입니다.
1 |
|
이렇게 모든 노드의 레벨을 lv
배열에 저장해놓고, 값을 출력해보면 다음과 같습니다.
1 |
|
1 |
|
우리가 원하는 값은 모든 사절들의 행복함의 최대 합계이므로, 매 순간마다 lv
값이 가장 큰 도시를 k
번 선택합니다. (greedy) 지난시간에 배운 분할정복
을 이용한 merge sort
또는 quick sort
로 레벨값을 내림차순 정렬한 후 k
개 만큼의 합을 구하면 될 듯 합니다. nlogn
의 시간복잡도입니다.
C++
에는nlogn
시간복잡도로 정렬해주는 sort
함수가 #include <algorithm>
헤더 파일에 있으므로, 그걸 사용하겠습니다.
1 |
|
값이 11
이 나옵니다. 예제의 답 9
와 다르네요.
그 이유에 대해 생각해봅시다.
위의 lv
배열을 내림차순으로 정렬하면, {3,3,2,2,1,1,1,0}
이 됩니다. 따라서 k
가 5
로 주어졌으므로, 3+3+2+2+1 = 11
을 출력한 것입니다. 이 경우를 그림으로 나타내봅시다.
위의 그림에서 4번 도시
, 8번 도시
를 고를때까진 문제가 없습니다. 하지만 5번 도시
와 3번 도시
를 고를때,
이 도시들이 관광도시에서 산업도시로 바뀌는 과정에서 4번 사절
과 8번 사절
의 행복함이 1씩 감소하게됩니다.
그렇다면, x
번 도시가 산업도시로 선택됨으로 인해 행복함이 감소되는 사절의 수는 어떻게 구할까요?
그 답은 x
번 도시의 자손 노드의 수
입니다. 왜냐하면 우리는 레벨이 높은 도시부터 우선적으로 골라왔기때문에, 어떤 노드를 고를 때 그 자손 노드들은 이미 다 선택되어있을 것입니다. 따라서 다음과 같은 상황이 됩니다.
따라서 2+2+2+2+1 = 9
로 정확한 답이 됩니다. (위의 그림에서 7
을 고른다면 답보다 값이 작아집니다.)
답을 출력하기 위한 과정은 다음과 같습니다.
DFS
과정에서 각 노드의자손 노드
의 수를 저장하는descendant
배열을 만듭니다.- 도시
x
를 선택할 때 발생하는 행복함의 값은,lv[x]
-descendant[x]
입니다. (자손 노드의 수만큼 총 행복함 값이 감소하고, 자신의lv
값 만큼 행복함이 증가하므로) - 위의 값이 큰것부터
k
개를 뽑아 더한 값을 출력하면 됩니다.(greedy)
그 코드입니다.
1 |
|