publicclassP_81305{
// 몇번 그룹을 나눌지 저장하는 전역변수staticint cut = 0;
// 루트 노드(시험장) 번호저장staticint root;
// 연결 노드staticint[][] trees;
// 노드(시험장)의 학생수staticint[] amount;
privatestaticintsolution(int k, int[] num, int[][] links){
// 특정 x 명인 그룹이 최대로 k 개의 그룹이 나누어 지는지, 이 중의 k개 그룹으로 나눠지는 x의 최소 x 는 무엇인가?// x 의 범위는 num.max ≤ x ≤ num.sumint start = 0;
int end = 0;
int limit;
trees = links;
amount = num;
// 루트 노드 찾기
findRoot(num.length);
// 이진 탐색 범위// start : 노드의 최댓값// end : 노드들의 합for (int n : num) {
start = Math.max(n, start);
end += n;
}
// 이진 탐색 시작while (start<end) {
limit = (start + end)/2;
if (getGroup(limit) <= k) {
end = limit;
} else {
start = limit+1;
}
}
return start;
}
// root 노드 찾는 함수privatestaticvoidfindRoot(int size){
boolean[] notRoot = newboolean[size];
// root 를 찾기// 자식이 없으면 rootfor (int[] tree : trees) {
if (tree[0] != -1) notRoot[tree[0]] = true;
if (tree[1] != -1) notRoot[tree[1]] = true;
}
for (int i=0; i<notRoot.length; i++) {
if(!notRoot[i]) {
root = i;
break;
}
}
}
// 개별 그룹의 최대 학생수 limit 을 안넘는 그룹 개수privatestaticintgetGroup(int limit){
cut = 0;
DFS(root, limit);
// 총 그룹수는 나눈 횟수 + 1return cut+1;
}
// 시험잠 탐색privatestaticintDFS(int node, int limit){
int left, right;
left = (trees[node][0] == -1) ? 0 : DFS(trees[node][0], limit);
right = (trees[node][1] == -1) ? 0 : DFS(trees[node][1], limit);
// 1. 그룹이 나뉘어지지 않는다.(N + L + R ≤ limit)if(amount[node] + left + right <= limit) {
return amount[node] + left + right;
}
// 2. 2그룹으로 나눈다. // 2그룹으로 나눌수 있는 조건이 한번은 cut 나야하니까 (N + L + R ≥ limit 이기 때문에)// node + min ≤ limit 값이면 1번만 cut. 이외면 2번 cut.if (amount[node] + Math.min(left, right) <= limit) {
cut+=1;
return amount[node] + Math.min(left,right);
}
// 3. 이외에는 3그룹으로 나눈다.
cut += 2;
return amount[node];
}
}