1358. 分割树

1358. 分割树

现在有一棵树T,有N个节点,我们想通过去掉一个节点p来把T分割成更小的树,并且满足每个小树中的节点数不超过n/2。

请根据输入的树来输出所有可能的p的号码。

树形dp

在遍历树的过程中,访问每个node,维护两个值:

  1. 所有子树的结点数的最大值childmax
  2. 所有子树(这里包括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;
}
原文地址:https://www.cnblogs.com/liulex/p/11394298.html