PAT甲级1021解法
原题链接
https://pintia.cn/problem-sets/994805342720868352/problems/994805482919673856
思路
这题实际上包含了两个问题,一个是判断给出的图是不是树,另一个是找出最深的根节点。
对于第一个问题,网上有一些做法是从任意结点开始深度遍历,看能否一次遍历完所有结点,相当于一个连通图,如果不能,则总共需要遍历的次数即联通子图的个数。我的做法与此不同,在每次读入时判断两个端点是否都已经被访问过,如果不是,则记录两个端点为已访问,如果是,说明图不是树,并且将联通子图的个数加一。大家可以在纸上画一画看看是不是这样。
对于第二个问题,我的方法是对每个结点深度遍历,每下一层则记录的depth加1,如果depth比maxDepth大,则更新maxDepth为depth。对每个结点计算出的maxDepth做比较,将最大的几个结点插入结果集。这里有一个问题,一开始不知道最大的是几,有可能第一个就是最大了,所以插入结点分为大于和等于两种情况。具体详见代码
遇到的问题是在第3个测试点时提示内存超限。参考了一下网上的方法,在不破坏自己其他算法的情况下,将记录图的数据结构从邻接矩阵换成了邻接表。发现C++实现不包含取值信息的邻接表其实非常简单,和动态二维数组基本没什么区别,或者可以说是一种简化的动态二维数组。之前我们使用的是vector<vector
代码
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int> > graph;
int maxDepth = 0;
vector<int> deepestRoot;
vector<int> reached;
int findDeepest(int root, int N, int depth);
int main()
{
int N;
cin >> N;
graph.resize(N + 1);
reached.assign(N + 1, 0);
bool isTree = true;
int connected = 1;
for (int i = 0; i < N - 1; i++)
{
int a, b;
cin >> a >> b;
if (reached[a] == 1 && reached[b] == 1) //两个点之前都已经访问过
{
isTree = false;
connected++;
}
else
{
reached[a] = reached[b] = 1;
graph[a].push_back(b);
graph[b].push_back(a);
}
}
if (isTree == false) //不是树
cout << "Error: " << connected << " components";
else
{
int deepst = 0;
for (int i = 1; i <= N; i++)
{
maxDepth = 0;
reached.assign(N + 1, 0);
int temp = findDeepest(i, N, 0);
if (temp > deepst)
{
deepst = temp;
deepestRoot.clear();
deepestRoot.push_back(i);
}
else if (temp == deepst)
{
deepestRoot.push_back(i);
}
}
for (int i = 0; i < deepestRoot.size(); i++)
{
cout << deepestRoot[i] << endl;
}
}
return 0;
}
int findDeepest(int root, int N, int depth)
{
reached[root] = 1;
depth++;
if (depth > maxDepth)
maxDepth = depth;
for (int i = 0; i < graph[root].size(); i++)
{
int t = graph[root][i];
if (reached[t] == 0) //有通路且没有被访问过
{
findDeepest(t, N, depth);
}
}
return maxDepth;
}