문제
- 문제 링크
- 문제 설명: 직원들의 하루평균 매출액 값을 담은 배열 sales, 직원들의 팀장-팀원의 관계를 나타내는 2차원 배열 links가 매개변수로 주어집니다. 이때, 모든 팀에서 최소 한 명 이상 워크숍에 참석하면서, 참석하는 직원들의 하루평균 매출액의 합을 최소로 하려고 합니다. 그렇게 최소화된 매출액의 합을 구해서 반환하는 함수를 작성
- 두 팀에 속한 1명은 팀장 이면서 팀원 이어야 함 -> 참석하면 두 팀이 참석한 것
- CEO는 항상 1번
- 입력
- sales 배열의 크기는 2 이상 300,000 이하
- sales 배열의 크기는 CEO를 포함한 전체 직원 수
- sales 배열의 각 원소의 값은 0 이상 10,000 이하인 정수
- links 배열의 크기는 sales 배열의 크기 - 1
- links 배열의 각 원소는 [a, b] 형식, a는 팀장의 직원번호, b는 a팀장이 관리하는 팀원의 직원번호
- links 배열로 만들어지는 조직도는 하나의 트리 구조 형태
- 출력: 모든 팀이 워크숍에 참석하도록 할 때 매출액의 최소합
sales | links | result |
[14, 17, 15, 18, 19, 14, 13, 16, 28, 17] | [[10, 8], [1, 9], [9, 7], [5, 4], [1, 5], [5, 10], [10, 6], [1, 3], [10, 2]] | 44 |
[5, 6, 5, 3, 4] | [[2,3], [1,4], [2,5], [1,2]] | 6 |
[5, 6, 5, 1, 4] | [[2,3], [1,4], [2,5], [1,2]] | 5 |
[10, 10, 1, 1] | [[3,2], [4,3], [1,4]] | 2 |
풀이
- sales 배열이 크기 때문에 노드를 최소 1번 또는 상수 횟수로 탐색해야 함.
- 부모-자식 관계 == 팀 (루트: 팀장[ceo], 비단말: 팀장/팀원, 단말: 팀원)
- 모든 팀을 포함하도록 노드를 선택할 때 최소합 = min(자신이 참석한 경우 현재 트리의 최소합, 참석안할 때 현재 트리의 최소합)
- DFS의 반환값: (자신이 참석한 경우 현재 트리의 최소합, 참석안할 때 현재 트리의 최소합) = (x,y)
- 단말 노드: (자신의 노드값, 0) = (x,y)
- 비단말, 루트 노드
- x = 자신의 노드값 + sum(min(서브트리에서 자식이 참석o 최소합, 자식이 참석x 최소합))
- y = min(i번째 자식이 참석o 최소합 + sum(min(j번째 자식이 참석o 최소합, j번째 자식이 참석x 최소합)))
- for 모든 자식 노드, i != j
- 비단말 노드는 자식 노드를 1번씩 모두 순회해야 되므로 DFS로 트리의 노드를 2번씩만 탐색하게 된다. → O(N)
- 주의할 점: 각 원소가 최대 10000을 가지고, sales의 전체 크기가 300,000 이므로 중간 결과가 정수 자료형의 범위를 벗어날 수 있음
- 정답 코드
#include <string>
#include <vector>
#define min(x,y) x < y ? x : y
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
vector<vector<int>> adj;
pll dfs(int root, vector<int>& sales) {
pll ret = {sales[root], 0};
if (adj[root].size() == 0) return ret;
vector<pll> subtrees;
for (auto& child : adj[root]) {
pll subtree = dfs(child, sales);
// 자신이 참석한 경우 서브 트리의 두 경우 중 작은 것을 포함
ret.first += min(subtree.first, subtree.second);
subtrees.push_back(subtree);
}
ret.second = 1e9;
for (int i = 0; i < subtrees.size(); i++) {
int minSum = subtrees[i].first; // i번째 자식이 반드시 참석한 경우
// 그 외 다른 서브 트리의 최소합의 합을 구함
for (int j = 0; j < adj[root].size(); j++) {
if (i == j) continue;
minSum += min(subtrees[j].first, subtrees[j].second);
}
// 모든 자식들 중 서브트리의 최소합이 가장 작은 것 계산
ret.second = min(ret.second, minSum);
}
return ret;
}
int solution(vector<int> sales, vector<vector<int>> links) {
adj = vector<vector<int>> (sales.size(), vector<int>());
for (auto& v : links) {
adj[v[0]-1].push_back(v[1]-1);
}
pll ret = dfs(0, sales);
return min(ret.first, ret.second);
}
'Algorithm > Problem Solving' 카테고리의 다른 글
Google Code Jam 2021 Round 1B (0) | 2021.04.26 |
---|---|
Google Code Jam 2021 Round 1A (0) | 2021.04.26 |
[구현] (프로그래머스 Lv 4) 블록 게임 (0) | 2021.04.24 |
[PriorityQueue] (프로그래머스 Lv 3) 셔틀버스 (0) | 2021.04.24 |
[구현] (프로그래머스 Lv 3) 추석 트래픽 (0) | 2021.04.24 |