题意:
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;
}
转载请注明出处!!!
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢