猫史档案馆


【直击OI】【C++】基础树上问题——树的直径

用户:爵士OIer爵士OIer查看:0 回复:1 评论:0 创建时间:2020-04-18T09:46:14


【直击OI】【C++】基础树上问题——树的直径

 

 

概念

树中所有最短路径距离的最大值即为树的直径。

 

举个栗子,树(假定每条边的权值都是1)

1

/  \

2     3

的最长路径就是

2-1-3

所以它的直径就是2.

 

错误示范

 

请尽量反其道而行之。

 

A:多源最短路?跑一跑floyd不就行了?这么简单!

真是秉承了喵出奇迹的精神……

扑街*1

 

B:树上每个点对都求一遍LCA不就得了?这么简单!

您太客气了……

扑街*2

 

算法解析

 

喵算法,O(n^2),太巨(雾

 

论记忆化搜索与动态规划

实际上dp就是一种按简单循环规律的记忆化搜索,每次记录答案,两种算法的复杂度是相同的。

 

经典算法1:树形dp

考虑一棵树的直径与其根的关系,它要么经过根,要么完全在根的一个子树里。

如果直径经过根,考虑根将直径分成的两个部分,它们是子树中从根向下的最长的两条路径。(最长和次长)

对于直径不经过根(在根的一个子树内)的情况递归求解即可。

令f[i]为从i向下的最长路径长度,dfs过程中统计即可。

 

代码详解

 

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100007;
vector<int> E[MAXN];
int n, ans;
int f(int x, int fa) {
    int m1 = 0, m2 = 0;            //最长和次长
    for (int i = 0; i < E[x].size(); ++i)
        if (E[x][i] != fa) {
            int r = f(E[x][i], x)+1;
            if (m1 < r) m2 = m1, m1 = r;
            else if (m2 < r) m2 = r;
        }
    if (ans < m1 + m2) ans = m1 + m2;
    return m1;
}
int main() {
    cin >> n;
    for (int i = 1; i < n; ++i) {
        int u, v; cin >> u >> v;
        E[u].push_back(v);
        E[v].push_back(u);
    }
    f(1, -1);
    printf("%d\n", ans);
    return 0;
}

 

 

经典算法2:两遍dfs!

 

随便选一个点x,求出距离x最远的一个点y,然后再求出距离y最远的一个点z,yz一定是一条直径。

两遍dfs即可。

证明:关键是证明y一定是某一条直径的一个端点。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100007;
vector<int> E[MAXN];
int x,y,n,de[MAXN];
void f(int x, int fa) {
    de[x]=de[fa]+1;
    for (int i = 0; i < E[x].size(); ++i)
        if (E[x][i] != fa)
            f(E[x][i],x);
}
int main() {
    cin >> n;
    for (int i = 1; i < n; ++i) {
        int u, v; cin >> u >> v;
        E[u].push_back(v);
        E[v].push_back(u);
    }
    f(1,0);x=1;
    for(int i=2;i<=n;i++)
        if(de[i]>de[x]) x=i;
    f(x,0);y=1;
    for(int i=2;i<=n;i++)
        if(de[i]>de[y]) y=i;
    cout<<de[y]-1;            //这里必须注意!因为y算了两次。
    return 0;
}

 

爵士精品,直击OI !

center_image


回复

上一页1 页 / 共 1下一页
爵士OIer爵士OIer

好帖,定了!!!!

点赞0


评论