这里的树是指无根树。
样例输入8 1 2 1 3 1 4 4 5 3 6 6 7 7 8样例输出
6
解法1:先将无根树转有根树(任选一点做根),然后用DFS找到离根最远的节点,则此节点必为最远点对中的一个。然后再以此节点为根将该树转有根树,找到离其最远的节点,其距离即为最长路径。
注意: 第2次转有根树的时候要将新根的parent节点清成-1,不然它还是用上次的parent节点。
#include <iostream>
#include <vector>
#define N 1000000+10
vector<int> G[N]; //stores the graph
int parent[N];
int n;
void read_tree()
{
int u, v;
cin>>n;
for (int i = 0; i < n - 1; i++) //注意无向图n个节点,边数为n-1
{
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
}
void dfs_root_tree(int u, int father)
{
for (int i = 0; i < G[u].size(); i++)
{
if (G[u][i] != father) { //加此判断,防止无限递归
parent[G[u][i]] = u;
dfs_root_tree(G[u][i], u);
}
}
}
int end_root;
int max_depth=0;
void dfs_find_longest_node(int u, int father, int depth) {
if (max_depth < depth ) {
max_depth = depth;
end_root = u;
}
if (G[u].size()==0) return;
for (int i=0; i<G[u].size(); i++) {
if (G[u][i] != father) {
dfs_find_longest_node(G[u][i], u, depth+1);
}
}
return;
}
int main()
{
read_tree();
int root;
parent[root] = -1;
dfs_root_tree(root, -1);
//for (int i = 1; i <= n; i++)
// cout << parent[i] << ' ';
//cout<<endl;
dfs_find_longest_node_1(root, parent[root], 0);
cout<<"end_root is "<<end_root<<endl;
parent[end_root] = -1;
dfs_root_tree(end_root, -1);
//for (int i = 1; i <= n; i++)
// cout << parent[i] << ' ';
//cout<<endl;
dfs_find_longest_node(end_root, parent[end_root], 0);
cout<<"end_root is "<<end_root<<endl;
cout<<"len = "<<max_depth<<endl;
return 0;
}
该解法稍嫌繁琐,因为要转两次有根树。实际上我们不用parent[]也可以,因为可以用vis[]来表示有没有访问过,也就是说不需要转换成有根树了。简化解法下次再写。
解法2:设d[i]为根为节点i的子树中根到叶子的最大距离。我们选一个子节点数>=2的节点作为root (如果儿子数都是<=1,那么这就是一个简单的link list,return n-1 就可以了),然后对每个节点i, 要记录该节点下面的子节点们的d[]里面的前两个最大的,比如说d[u]和d[v]。一次DFS后,遍历所有节点,找出它们所记录的d[u]+d[v]的最大值,然后+2即可。
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define N 1000000+10
vector<int> G[N]; //stores the graph
int n;
void read_tree()
{
int u, v;
cin>>n;
for (int i = 0; i < n - 1; i++) //注意无向图n个节点,边数为n-1
{
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
}
int d[N] = {0};
int vis[N] = {0};
struct two_longest_children{
int longest_depth;
int second_longest_node;
}record[N];
void dfs_find_longest_node_2(int u) {
vis[u] = 1;
if (G[u].size()==0) {
d[u] = 0;
return;
}
for (int i=0; i<G[u].size(); i++) {
if (!vis[G[u][i]]) {
dfs_find_longest_node_2(G[u][i]);
int temp = d[G[u][i]];
if (d[u] <= temp+1) {
d[u]= temp+1;
}
if (record[u].longest_depth <= temp) {
record[u].second_longest_depth = record[u].longest_depth;
record[u].longest_depth = temp;
}
if ((record[u].second_longest_depth <= temp) && (record[u].longest_depth > temp)) {
record[u].second_longest_depth = temp;
}
}
}
return;
}
int main()
{
int root=0xFF;
read_tree();
memset(record,0,sizeof(record));
for (int i=1; i<n; i++) {
if (G[i].size()>=2) {
root=i;
break;
}
}
if (root==0xFF) return n-1;
dfs_find_longest_node_2(root);
int g_max_len = 0;
for (int i=1; i<=n; i++) {
if (g_max_len <= record[i].longest_depth+record[i].second_longest_depth + 2) {
g_max_len = record[i].longest_depth+record[i].second_longest_depth + 2;
}
}
cout<<"g_max_len is "<<g_max_len<<endl;
return 0;
}
注意:
1) 用该方法最长路径并不一定经过根。看看下面的图就知道了。 所以我们要记录所有的节点的子节点d[]里面的最大的两个值。上面的办法可行是因为用了两次转换成有根树。
2) 跟第一种方法相比,该方法有个缺点就是不能记录到底是哪两个节点的最远对。因为dfs_find_longest_node_2()里面每个节点只能知道自己哪两个子节点有最长的depth,但不能一直跟踪到叶子节点。