1358. 分割树
现在有一棵树T,有N个节点,我们想通过去掉一个节点p来把T分割成更小的树,并且满足每个小树中的节点数不超过n/2。
请根据输入的树来输出所有可能的p的号码。
树形dp
在遍历树的过程中,访问每个node,维护两个值:
- 所有子树的结点数的最大值childmax
- 所有子树(这里包括node)的结点数之和sum。
递归过程中用上一层的sum,不断更新这一层的childmax。
而childmax和sum则共同用来判断这个node是否可以删除。
下面再分析判断条件: childmax<=n/2 && n-sum<=n/2
childmax<=n/2 :去掉node后,原先node的子树均满足条件。
n-sum<=n/2 :去掉node后,原先除node和node的所有子树外的树(就当是node的祖先树吧)均满足条件。
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; typedef long long LL; #define mst(s, t) memset(s, t, sizeof(s)) const int INF = 0x3f3f3f3f; const int maxn = 10010; vector<int> G[maxn]; int ans[maxn], num, n; int dfs(int node, int father){ int sum = 1, childmax = 0; //若是叶子结点则return sum=1,否则求其子树(包括自己)的总结点数 for(int i=0; i<G[node].size(); i++){ if(G[node][i] == father)continue; //因为是树结构,这里可以在无向时避免遍历成环 int sum_son = dfs(G[node][i], node); childmax = max(sum_son, childmax);//所有子树的结点数的最大值 sum += sum_son;//sum:node的子树的结点数和 } childmax = max(childmax, n-sum); if(childmax <= n/2){ /* * 当node结点的孩子结点的结点数最大为Sum,若Sum<=n/2,则该点符合条件 * 因为去掉node后,任意子树结点数<=n/2, max()保证其非子树结点和仍<=n/2 * 故该点满足条件 */ ans[num++] = node; } return sum; } int main() { //freopen("in.txt", "r", stdin); scanf("%d", &n); for(int i=0; i<n-1; i++){ int a, b; scanf("%d%d", &a, &b); G[a].push_back(b); G[b].push_back(a); } num = 0; int tmp = dfs(1, 0); //cout << n << "==" << tmp << endl; //验证 sort(ans, ans+num); if(num){ for(int i=0; i<num; i++){ printf("%d ", ans[i]); } }else{ printf("NONE "); } return 0; }