쉽지않은 블로그
백준온라인 13209 [검역소][C++] 본문
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(10^9 ) \) 안에 수행할수 있다.
주의할 점
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";
}
}
'알고리즘문제풀이 > 백준온라인' 카테고리의 다른 글
백준온라인 17835 [면접보는 승범이네][C++] (0) | 2021.03.24 |
---|---|
백준온라인 17831번 [대기업 승범이네] [C++] (0) | 2021.03.24 |
백준온라인 5052번 [전화번호 목록][C++] (0) | 2021.03.19 |
백준온라인 2618번 [경찰차][C++] (0) | 2021.03.17 |
백준온라인 2306번 [유전자][C++] (0) | 2021.03.16 |
Comments