P3915 树的分解

题意:给你一个含有n个结点的树(无向无环图,这个条件非常重要),判断能否按照每组k个结点,将其分成n/k组,并且要求每组仍然连通

结论:如果一个树可以被分成满足以上条件的组,那么从任意一个点出发(以任意一个结点为根)都可以将树按照上面的条件分块,并且分法唯一

将树按照无向图存储(保证任意一个结点都可以作为根,从他出发可以到达树上的任意一个结点),然后选择1号结点作为根递归求以每一个结点为根的子树大小,每当计算得到的子树大小为k时,说明找到了一组,此时令他向上一层返回的值为0,表示它的双亲结点不能和他再组合了,即他对它的双亲结点的数目贡献为0。

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

const int N = 100010;

int h[N], e[N << 1], ne[N << 1], idx;
int T;
int n, k, g;
int cnt[N];

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int dfs(int u, int v){
    int sum = 1;

    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j != v) sum += dfs(j, u);
        // 此处不同
        if(sum == k){
            g ++;
            return 0;
    	}
    }
    
    return sum;
}

int main(){
    cin >> T;
    
    while(T --){
        memset(h, -1, sizeof h);
        idx = 0;
        g = 0;
        
        cin >> n >> k;
        for(int i = 0; i < n - 1; i ++){
            int a, b;
            cin >> a >> b;
            
            add(a, b), add(b, a);
        }

        if(n % k){
            puts("NO");
            continue;
        }
        
        dfs(1, 0);
        
        if(g == n / k) puts("YES");
        else puts("NO");
    }
    
    return 0;
}

上面的代码在这个例子下会出错,而实际上wa了一个点...

1
3 1
1 2
1 3

输出NO,而正确答案是YES

从1开始遍历,先到3,在3处计算得到子树大小为1,满足条件,g++并返回0,回到1所在层,sum+0=sum=1, 发现sum=1,令g++,并返回0,这样的话,最终g=2,因为结点2没有被遍历到。

#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;

const int N = 100010;

int h[N], e[N << 1], ne[N << 1], idx;
int T;
int n, k, g;
int cnt[N];

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int dfs(int u, int v){ // 因为按照无向图存储,所以需要存储u是从哪走过来的,避免无限递归
    int sum = 1;

    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j != v) sum += dfs(j, u);
    }
    
    if(sum == k){
        g ++;
        return 0;
    }
    
    return sum;
}

int main(){
    cin >> T;
    
    while(T --){
        memset(h, -1, sizeof h);
        idx = 0;
        g = 0;
        
        cin >> n >> k;
        for(int i = 0; i < n - 1; i ++){
            int a, b;
            cin >> a >> b;
            
            add(a, b), add(b, a);
        }

        if(n % k){
            puts("NO");
            continue;
        }
        
        dfs(1, 0);
        
        if(g == n / k) puts("YES");
        else puts("NO");
    }
    
    return 0;
}

复杂度(O(NT))

原文地址:https://www.cnblogs.com/tomori/p/15050307.html