Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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 31
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(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";
    }
}
Comments