POJ-1655: Balancing Act
keyword
グラフ 木 C++
問題概要
ノード数N(<20000)の木が与えられる。各ノードvに対してbalance(v)を、G-{v}の連結成分のもっとも大きい値と定義する。balance(v)を最小にするノードvとその値を求める問題。
解法
葉から順次処理していく。処理した葉はグラフから取り除き、新たに生じた葉をキューに突っ込んでいけばよい。計算量O(N)。
枝をkeyにしてメモ化再帰でといても良さげ。
#include <cstdio> #include <vector> #include <queue> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 20009; vector<int> G[MAX_N]; int balance[MAX_N]; int childs[MAX_N]; int degs[MAX_N]; bool visited[MAX_N]; int N; void subsolve(){ queue<int> Q; for(int i=0; i<N; i++)if(degs[i]==1){ Q.push(i); } while(!Q.empty()){ int tp = Q.front(); Q.pop(); visited[tp] = true; vector<int>& es = G[tp]; int ma = 0, sum = 1; for(int i=0; i<(int)es.size(); i++){ int u = es[i]; if(visited[u]){ ma = max(ma, childs[u]); sum += childs[u]; } else if(--degs[u]==1){ Q.push(u); } } balance[tp] = max(ma,N-sum); childs[tp] = sum; } } void solve(){ int key = -1, best = 1<<20; subsolve(); for(int i=0; i<N; i++){ if(best > balance[i]){ best = balance[i]; key = i; } } printf("%d %d\n", key+1,best); } int main(){ int T; for(scanf("%d",&T);T--;){ scanf("%d",&N); memset(degs,0,sizeof(degs)); memset(balance, 0, sizeof(balance)); memset(childs, 0, sizeof(childs)); memset(visited, 0, sizeof(visited)); for(int i=0; i<N; i++){ G[i].clear(); } for(int i=0; i<N-1; i++){ int x, y; scanf("%d%d",&x,&y); x--; y--; G[x].push_back(y); G[y].push_back(x); degs[x]++; degs[y]++; } solve(); } return 0; }