poj1655-又是一道简单而又纠结的题目

题目很好理解,就是去掉树上的一个节点,看看剩下的子树中最大的是多少,然后在这些最大值中求一个最小值,如果有多个点都是最小值,那么找一个序号最小的节点。

输出节点号,和最小值。

 

经过简单分析,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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值