题目很好理解,就是去掉树上的一个节点,看看剩下的子树中最大的是多少,然后在这些最大值中求一个最小值,如果有多个点都是最小值,那么找一个序号最小的节点。
输出节点号,和最小值。
经过简单分析,dfs深度优先搜索可以解决,只需要求出每个节点下子树的总结点个数即可。
举例说明:
设有一棵树20个节点,其中有一个节点为u,u有两个孩子节点,设u以下有10个节点,两个孩子分别有6和4个节点,那么对于u来说,最大是多少,应该是20 - 10,6,4中的最大的也就是10.这样等把所有节点的最大值求出后,再求1-n中的最小值,输出该点以及最小值即可。
算法就是dfs,计算出每个节点下的总数,然后保留本节点下的孩子节点子树中的最大值,然后和自己的祖先节点比较求出最大值,最后枚举最小值。
这题wa了很多次,感觉dfs没问题,后来确实发现时建图的问题。一定要建一个双向图,因为树是无向的。
开始我只按照题目给的建了一个图,
如果碰到下面的例子,比如有5个点,1一圈有四个点
也就是
5
4 1
3 1
2 1
5 1
这样的话当搜索到1时就终止了,没法通过1搜到其他点。所以建双向,然后判是否走过就行了。
纠结了好久。。。。
下面是代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define nMax 20010
struct Edge{
int v;
int next;
}edge[2 * nMax];//保存双向图
int preEdge[nMax];//同一个起点的上一条边
int n;
bool flag[nMax];
int childNum[nMax];//每个节点下的节点总数
int min;
int childMax[nMax];//每个节点去掉后其他子树最多点数
int dfs(int root)
{
flag[root] = true;
int p = preEdge[root];//以root为起点的边的标示也就是edge的下标
while (p)
{
int v = edge[p].v;//root连接的点
if (!flag[v])
{
childNum[root] += dfs(v);//求root点下的所有节点
childMax[root] = Max(childNum[edge[p].v], childMax[root]);//求root点下孩子节点中的最大值
}
p = edge[p].next;//以root为起点的上一条边
}
childMax[root] = Max(n - childNum[root], childMax[root]);//root下孩子节点中的最大值和祖先节点总数的最大值作为本节点的最大值
return childNum[root];//返回本节点的总数
}
int main()
{
int t;
int u, v;
while (scanf("%d", &t) != EOF)
{
while (t --)
{
memset(preEdge, 0, sizeof(preEdge));
memset(flag, false, sizeof(flag));
min = 0x0FFFFFFF;
scanf("%d", &n);
for (int i = 1; i < 2 * (n - 1); i += 2)//建图
{
scanf("%d %d", &u, &v);
edge[i].v = v;
edge[i].next = preEdge[u];
preEdge[u] = i;
edge[i + 1].v = u;
edge[i + 1].next = preEdge[v];
preEdge[v] = i + 1;
}
if (n == 1)
{
printf("1 0\n");
continue;
}
//memset(childNum, 0, sizeof(childNum));
for (int i = 1; i <= n; ++ i)
{
childNum[i] = 1;
childMax[i] = -1;
}
dfs(1);//dfs
int mMin = 0x0FFFFFFF, k;
for (int i = 1; i <= n; ++ i)//求最小值
{
if (mMin > childMax[i])
{
k = i;
mMin = childMax[i];
}
}
printf("%d %d\n", k, mMin);
}
}
return 0;
}