- 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
题目大意:给你一个图,有n个节点,n-1条边。求出以其中某一点为根时,构造的树有最大的深度。如果有多个点构造的树的深度都是最大的,那么依次输出;如果该图不是连通图,则输出连通域的个数
分析:跟深度有关,可以选择用bfs。dfs也可以,每次返回访问的点的深度即可,代码量比bfs小。每个点当一次根,每次计算深度时都要访问n个节点,那么时间复杂度为O(N^2)。题目限时1.5s,所以当极限情况下很可能超时,必须要剪枝。
注意到,如果第一次遍历图之后,发现这个图不是连通图,即图中还有未访问过的节点,那么接下来就没必要再把其他点作为根计算深度了。此时由于图中剩余的点都还没访问,所以接下来应当对这些点bfs,把它所在的连通域都访问一遍,再判断是否还有未访问的点,如此往复到所有点都访问完。 每个点都仅被访问一次,那么O(N)。
一开始我的做法是遍历时用并查集统计连通域个数。这样的话,不管该图是否是连通图,依旧会计算所有点作为根的深度,大大浪费时间,导致我有一个case超时了。然后看了别人的题解,才明白
对于题目的限时,假设是1s,我们的时间复杂度不能超过千万级别,如果是o(n^2),则n不能超过3000。 本题是1.5s的限时,但是n是绝对不能超过10000的,所以必须要剪枝。当然,如果真的有n=10000,且是连通图的case,那单纯对所有点用图的遍历就没法做了。
源码如下:
#include<stdio.h>
#include<vector>
#include<queue>
using namespace std;
struct Edge{
int next;
};
int layer[10001]; //存储以某个节点的深度
bool mark[10001]; //标记访问过
vector<Edge> edge[10001];
queue<int> Q;
int result[10001]; //存储以某个节点为根的树的深度
int maxLayer = 0; //最大的深度
int componentCount = 1; //连通域的个数,至少有一个
int n;
void init(){
for(int i=1; i<=n; i++){
layer[i] = 1;
mark[i] = false;
}
}
void bfs(int node){
mark[node] = true;
Q.push(node);
int currentPoint;
int tmpMaxLayer = 0; //临时存放该树的深度
while(!Q.empty()){
currentPoint = Q.front();
Q.pop();
for(int i=0; i<edge[currentPoint].size();i++){
if(!mark[edge[currentPoint][i].next]){ //没访问过
mark[edge[currentPoint][i].next] = true;
layer[edge[currentPoint][i].next] = layer[currentPoint] + 1;
Q.push(edge[currentPoint][i].next);
if (tmpMaxLayer < layer[edge[currentPoint][i].next])
tmpMaxLayer = layer[edge[currentPoint][i].next];
}
}
}
result[node] = tmpMaxLayer;
if(tmpMaxLayer > maxLayer)
maxLayer = tmpMaxLayer;
}
int main(){
int a,b;
scanf("%d", &n);
for(int i=0; i<n-1; i++){
scanf("%d%d",&a,&b);
Edge tmp;
tmp.next = a;
edge[b].push_back(tmp);
tmp.next = b;
edge[a].push_back(tmp);
}
for(int i=1; i<=n; i++){
if(componentCount == 1) //如果经过第一次检查发现是连通图,那么初始化后,计算以下一个点位根的树的深度
init();
bfs(i);
for(int j =1; j<=n; j++){ //检查是否是连通图,如果不是连通图则dfs(j),此时就不是遍历所有点计算深度了,而是转而为接下来统计连通域的个数做准备
if(!mark[j]){
componentCount ++;
i = j-1;
break;
}
}
}
if(componentCount != 1)
printf("Error: %d components\n", componentCount);
else {
for(int i= 1; i<=n; i++)
if(result[i] == maxLayer)
printf("%d\n", i);
}
return 0;
}
总结:
1.要提高代码的效率,避免无谓的无用功
2.注意限时
3.不要老用bfs了,下次遇到和深度有关的试试dfs
4.我觉得一开始用并查集太蠢了。。。。