쉽지않은 블로그
백준온라인 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이하로 만들 수 있는지 없는지에 대해서 이분 탐색을 수행해주면
최대
주의할 점
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 |