Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

쉽지않은 블로그

백준온라인 13209 [검역소][C++] 본문

알고리즘문제풀이/백준온라인

백준온라인 13209 [검역소][C++]

쉽지않네 2021. 5. 20. 16:08

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

 

13209번: 검역소

3번 도시와 5번 도시를 잇는 도로와 4번 도시와 3번 도시를 잇는 도로에 검역소를 설치하면 치료제를 11 인분만 비축해도 된다. 1번 도시에 전염병이 발생할 경우 1번 도시와 3번 도시의 10명의 사

www.acmicpc.net


나의 풀이

이 문제는 간단하게 말하면

트리구조의 자료구조에서 각 집 단안의 모든 노드 값의 합중 최댓값이 최소가 되게끔 집합을 묶었을 경우

집단내의 노드합이 최대값이 얼마인지를 묻는 문제이다.

 

 

이 문제는 10만개의 노드들을 어떻게 묶을지 선택을 해야 하는데 순차적으로 포함 여부를 모두 해보면 당연히 시간 초과가 날것이다

그리디나 DP 로 생각을 해보려고 했지만 하나를 선택함으로써 나머지가 변하는 side effect를 도대체 어떻게 고려해야 할지 몰랐다.

 

하지만 문제를 바꿔서 만들수 있는 집단의 개수가 주어질 때 집단내의 모든 합을 x이하로  만들 수 있는지 아닌지 

가능 불가능의 여부 문제로 바꿔보면 답은 이분탐색으로 귀결된다.

 

알고리즘

집단내의 모든 합을 x이하로 만들 수 있는지 없는지에 대해서 이분 탐색을 수행해주면

최대 O(N)log2(109) 안에 수행할수 있다.

 

주의할 점

int 형보단 long long 타입을 사용하는 게 좋을 거 같다.

이분 탐색의 left 값을 모든 노드 값 중 최대의 값을 선택해주어야 한다

// 제일 큰 값보다는 커야 집단에 포함해줄 수 있는 식이 성립될 수 있음

 


소스코드

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ft first
#define sd second
#define all(x) (x).begin(), (x).end()
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using vi = vector<int>;
using vb = vector<bool>;
using vs = vector<string>;
using vd = vector<double>;
using vll = vector<ll>;
using vpii = vector<pii>;
using vpll = vector<pll>;
using vvi = vector<vi>;
using vvb = vector<vb>;
using vvll = vector<vll>;
using vvpii = vector<vpii>;
using vvpll = vector<vpll>;
using vvs = vector<vs>;
ll d[100001];
vi e[100001];
vi edir[100001];
bool chk[100001];
pll getGroupCount(int idx,ll maxCnt){ // first : 개별의 갯수 , second : 군집의 갯수
pll tmp = {(ll)d[idx],0LL};
chk[idx]=true;
vpll childs ;
for(auto next :edir[idx]){
if(!chk[next])
childs.push_back(getGroupCount(next,maxCnt));
}
sort(all(childs));
for(auto child : childs){
if(tmp.first + child.first <= maxCnt){
tmp.first += child.first;
}else{
tmp.second += 1;
}
tmp.second += child.second;
}
return tmp;
}
void go(int idx){
chk[idx]=true;
for(auto next : e[idx]){
if(!chk[next]){
edir[idx].push_back(next);
go(next);
}
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
ll t,n,k,x,y;
cin>>t;
while(t--){
cin>>n>>k;
k++;
memset(d,0,sizeof(d));
memset(chk,false,sizeof(chk));
ll sum=0,minVal=0;
for(int i=1;i<=n;i++){
cin>>d[i];
sum+=d[i];
minVal = max(minVal , (ll)d[i]);
e[i].clear();
edir[i].clear();
}
for(int i=0;i<n-1;i++){
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
/////그래프 -> 트리로.
go(1);
/////
//묶이는 명수들중 최대가 몇명일까?
ll l=minVal,r=sum,mid,ans=0x3f3f3f3f3f3f3f3f;
while(l<=r){
memset(chk,false,sizeof(chk));
mid = (l+r)/2;
auto result = getGroupCount(1,mid);
ll need = (result.first > 0 ? result.second+1 : result.second);
if( k >= need){
ans = min(ans , mid);
r = mid-1;
}else if(k<need){
l = mid+1;
}
}
cout<<ans<<"\n";
}
}