1. 程式人生 > >GYM 101964 C(二分答案)

GYM 101964 C(二分答案)

傳送門:

題意:

    給你一個n個結點的樹,其中有一部分結點是黑色的。現在讓你選取m個黑色結點,使得這些黑色結點所形成的集合的中,任意兩點間的最遠距離最小。讓你求出這個最遠距離。

題目分析:

    首先,題目中的”使得最大值最小“這句話就非常符合二分的條件了。因此我們考慮對答案(兩個黑點的最遠的距離)進行二分。

    現在就要考慮如何進行check。

    對於每一個二分值k,我們考慮先用bfs遍歷整顆樹,然後利用bfs的性質(距離當前點的所有已bfs過且小於等於d的那些點,他們之間兩兩距離也小於等於k),找出滿足距離等於k的對應的一個個點集。之後我們可以用dfs去遍歷這些點集,判斷點集中的黑點的個數與需要選取的黑點個數m之間的關係即可(如果數量>=n,則右指標左移動;反之左指標右移)。

程式碼:

#include <bits/stdc++.h>
#define maxn 105
using namespace std;
vector<int>vec[maxn];
int vis[maxn];//用vis陣列去區分點的不同的集合
int a[maxn];
queue<int>que;
int n,m;
int ans=0;
int dfs(int now,int fa,int all,int dis){
    int res=a[now];
    if(dis==all) return res;
    for(auto &it:vec[now]){
        if(!vis[it]||it==fa) continue;
        res+=dfs(it,now,all,dis+1);
    }
    return res;
}
bool check(int k){//二分的check,本質上為一個bfs
    memset(vis,0,sizeof(vis));
    while(!que.empty()) que.pop();
    que.push(1);
    while(!que.empty()){//bfs選取部分點集
        int now=que.front();
        que.pop();
        vis[now]=1;
        int tmp=dfs(now,0,k,0);//通過dfs獲取這個集合的黑點的個數
        if(tmp>=m) return 1;
        for(auto &it:vec[now]){
            if(vis[it]) continue;
            que.push(it);
        }
    }
    return 0;
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=0;i<n-1;i++){
        int from,to;
        scanf("%d%d",&from,&to);
        vec[from].push_back(to);
        vec[to].push_back(from);
    }
    int l=0,r=n;
    while(l<r){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
        //cout<<l<<" "<<r<<endl;
    }
    cout<<r<<endl;
}