PAT甲级练习1021. Deepest Root (25)

1021. Deepest Root (25)

时间限制
1500 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

A graph which is connected and acyclic can be considered a tree. The height of the tree depends on the selected root. Now you are supposed to find the root that results in a highest tree. Such a root is called the deepest root.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (<=10000) which is the number of nodes, and hence the nodes are numbered from 1 to N. Then N-1 lines follow, each describes an edge by given the two adjacent nodes' numbers.

Output Specification:

For each test case, print each of the deepest roots in a line. If such a root is not unique, print them in increasing order of their numbers. In case that the given graph is not a tree, print "Error: K components" where K is the number of connected components in the graph.

Sample Input 1:
5
1 2
1 3
1 4
2 5
Sample Output 1:
3
4
5
Sample Input 2:
5
1 3
1 4
2 5
3 4
Sample Output 2:
Error: 2 components
计算最长深度的问题,觉得可以用dfs来做,但是也想到会有超时的问题,因为没有终点,且对每个起点需要全部遍历,并且也不像算最小距离时可以剪枝减少运算。做完之后结果也证实了有一个节点运行超时。。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

int n, x, y, num=0;
int maxDepth=0, tmp=0;
int r[10001][10001];
bool visited[10001];
vector<int> lroot;

void dfs2(int x, int length){//计算长度
	visited[x] = true;
	if(length>tmp){
		tmp = length;
	}
	for(int i=1; i<=n; i++){
		if(visited[i]==false && r[x][i]==1){
			dfs2(i,length+1);
		}
	}
	visited[x] = false;
}

void dfs1(int x){//判断是否只有一个连通域
	visited[x] = true;
	for(int i=1; i<=n; i++){
		if(visited[i]==false && r[x][i]==1){
			dfs1(i);
		}
	}
}


int main(){
	
	scanf("%d", &n);

	for(int i=1; i<=n-1; i++){
		scanf("%d %d", &x, &y);
		r[x][y] = r[y][x] = 1;
	}

	for(int i=1; i<=n; i++){
		if(visited[i]==0){
			num++;
			dfs1(i);
		}
	}

	memset(visited, false, 10001);
	if(num>1){
		printf("Error: %d components", num);
	}else{
		for(int i=1; i<=n; i++){
			tmp=0;
			dfs2(i, 0);
			if(tmp>maxDepth){
				maxDepth = tmp;
				vector<int> y;
				lroot.swap(y);
				lroot.push_back(i);
			}else if(tmp==maxDepth){
				lroot.push_back(i);
			}
		}
	}
	for(int i=0; i<lroot.size(); i++){
		printf("%d\n", lroot[i]);
	}

	cin>>n;
	return 0;
}

评测结果

时间 结果 得分 题目 语言 用时(ms) 内存(kB) 用户
2月18日 22:21 部分正确 23 1021 C++ (g++ 4.7.2) 7 3840 shower

测试点

测试点 结果 用时(ms) 内存(kB) 得分/满分
0 答案正确 4 384 13/13
1 答案正确 4 384 2/2
2 答案正确 7 3840 5/5
3 运行超时     0/2
4 答案正确 4 432 2/2
5 答案正确 4 384 1/1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
看了一下别人的做法,发现一个有趣的证明:树的直径(最长路)的证明。即从随便一点出发,通过dfs找到最远的节点,再从这一点出发进行dfs即可找到该树中距离最远的点。相关证明可以看看

判断完连通域个数后,可以从任一点开始dfs一遍获得一个最远点的集合,在从该集合中任选一点再dfs一遍即可得到另一组集合,然后合并这两个就是所得的最长的点的集合。注意第二遍不需要对第一遍得到的集合中每一个都去dfs一遍。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>

using namespace std;

int n, x, y, num=0;
int maxDepth=0, tmp=0;
int r[10001][10001];
bool visited[10001];
vector<int> lroot;

void dfs2(int x, int length){//计算长度
	visited[x] = true;
	if(length>maxDepth){
		maxDepth = length;
		vector<int> y;
		lroot.swap(y);
		lroot.push_back(x);
	}else if(length==maxDepth){
		lroot.push_back(x);
	}

	for(int i=1; i<=n; i++){
		if(visited[i]==false && r[x][i]==1){
			dfs2(i,length+1);
		}
	}
	//visited[x] = false;
}

void dfs1(int x){//判断是否只有一个连通域
	visited[x] = true;
	for(int i=1; i<=n; i++){
		if(visited[i]==false && r[x][i]==1){
			dfs1(i);
		}
	}
}


int main(){
	
	scanf("%d", &n);

	for(int i=1; i<=n-1; i++){
		scanf("%d %d", &x, &y);
		r[x][y] = r[y][x] = 1;
	}

	for(int i=1; i<=n; i++){
		if(visited[i]==0){
			num++;
			dfs1(i);
		}
	}

	fill(visited, visited+10001, false);

	if(num>1){
		printf("Error: %d components", num);
	}else{
		visited[1] = true;
		dfs2(1, 0);
		vector<int> v1(lroot);
		//int k=lroot.size();//不需要每个都去dfs一遍
		//for(int i=0; i<k; i++){
		//	fill(visited, visited+10001, false);
		//	visited[lroot[i]] = true;
		//	dfs2(lroot[i], 0);
		//}
		fill(visited, visited+10001, false);
		dfs2(lroot[0], 0);

		v1.insert(v1.end(), lroot.begin(), lroot.end());
		sort(v1.begin(), v1.end());
		tmp = -1;
		for(int i=0; i<v1.size(); i++){
			if(v1[i]!=tmp){
				printf("%d\n", v1[i]);
				tmp = v1[i];
			}
		}
	}

	cin>>n;
	return 0;
}

评测结果

时间 结果 得分 题目 语言 用时(ms) 内存(kB) 用户
2月19日 14:05 答案正确 25 1021 C++ (g++ 4.7.2) 289 40704 shower

测试点

测试点 结果 用时(ms) 内存(kB) 得分/满分
0 答案正确 16 384 13/13
1 答案正确 7 284 2/2
2 答案正确 16 3840 5/5
3 答案正确 289 40704 2/2
4 答案正确 16 384 2/2
5 答案正确 9 384 1/1


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值