GYM 101964 C(二分答案)
阿新 • • 發佈:2018-12-23
傳送門:
題意:
給你一個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; }