HDU6228求边的左右节点数

题意:

Problem Description

Consider a un-rooted tree T which is not the biological significance of tree or plant, but a tree as an undirected graph in graph theory with n nodes, labelled from 1 to n. If you cannot understand the concept of a tree here, please omit this problem.
Now we decide to colour its nodes with k distinct colours, labelled from 1 to k. Then for each colour i = 1, 2, · · · , k, define Ei as the minimum subset of edges connecting all nodes coloured by i. If there is no node of the tree coloured by a specified colour i, Ei will be empty.
Try to decide a colour scheme to maximize the size of E1 ∩ E2 · · · ∩ Ek, and output its size.

Input

The first line of input contains an integer T (1 ≤ T ≤ 1000), indicating the total number of test cases.
For each case, the first line contains two positive integers n which is the size of the tree and k (k ≤ 500) which is the number of colours. Each of the following n - 1 lines contains two integers x and y describing an edge between them. We are sure that the given graph is a tree.
The summation of n in input is smaller than or equal to 200000.

Output

For each test case, output the maximum size of E1 ∩ E1 … ∩ Ek.

Sample Input

3
4 2
1 2
2 3
3 4
4 2
1 2
1 3
1 4
6 3
1 2
2 3
3 4
3 5
6 2

Sample Output

1
0
1

​ 翻译成汉语大概意思就是有一颗树,在树上的点涂色,每个点涂一种颜色,一共可以涂k种颜色,然后你需要把每种颜色的点按最短路径用该种颜色连起来,问涂k次的边最多有几条?

​ 第一行输入的是T代表有T组样例,每组样例第一行为n,k,分别代表树上有n个节点,可用k种颜色给节点涂色。之后n-1行a,b代表节点a到节点b之间有一条边

思路:

​ 我们可以把树转化为图,对于每个边进行dfs(利用记忆化搜索),分别求出该边左边和右边的节点数,只要都大于等于k就可以累加,累加和就是答案。时间复杂度,大概也就O(n)吧,因为记忆化搜索dfs多也多不多少。。

代码:

#include 
#include 
#include 
#include 
#define d int32_t
using namespace std;

struct node {
    d left, right;
}bian[200005];
d num[200005][2];         //num[i][0]存储第i个边左边的节点数,num[i][1]存储第i个边右边的节点数
vectorzcy[200005];     //zcy[i]存储以i点为端点的边的编号,zcy[i].size()即为边的个数

//利用dfs+记忆化搜索求编号为now的边的chooes(0为左,1为右)的节点数
int dfs(int now, int chooes) {
    if(num[now][chooes]) return num[now][chooes];       //记忆化搜索
    int ans = 1, t;
    if (chooes == 0) {
        t = bian[now].left;
    } else {
        t = bian[now].right;
    }
    for (int i = 0; i < zcy[t].size(); i++) {
        int next = zcy[t][i];
        if (bian[next].left == bian[now].left && bian[next].right == bian[now].right) {
            continue;
        }
        if (bian[next].left == t) {
            if (num[next][1]) {          //记忆化搜索
                ans += num[next][1];
            } else {
                ans += dfs(next, 1);
            }
        } else {
            if (num[next][0]) {          //记忆化搜索
                ans += num[next][0];
            } else {
                ans += dfs(next, 0);
            }
        }
    }
    num[now][chooes] = ans;              //记录中间状态
    return ans;
}

int main()
{
    int T, n, k;
    scanf("%d", &T);
    while (T--) {
        int ans = 0;
        scanf("%d%d", &n, &k);
        memset(num, 0, sizeof(num));
        for (int i = 1; i <= n; i++) {
            zcy[i].clear();
        }
        for (int i = 1; i < n; i++) {
            scanf("%d%d", &bian[i].left, &bian[i].right);
            zcy[bian[i].left].push_back(i);
            zcy[bian[i].right].push_back(i);
        }
        for (int i = 1; i < n; i++) {
            dfs(i, 0);    //每个边两个dfs,分别求左边和右边(由于有记忆化搜索,所以可以不用右边=节点数-左边)
            dfs(i, 1);
        }
        for (int i = 1; i < n; i++) {
            if (num[i][0] >= k && num[i][1] >= k) ans++;        //此时满足条件
        }
        printf("%d\n",ans);
    }
    return 0;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

你可能感兴趣的:(HDU6228求边的左右节点数)