给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径上的边权之和。树中最远的两个节点之间的距离被称为树的直径。连接这两点的路径被称为树的最长链。
练手模板题:http://www.joyoi.cn/problem/tyvj-1520
树的直径有两种求法,时间复杂度都是O(N).我们假设树以N个点N-1条边的无向图的形式给出,并存储在图中。
void add(int u,int v,int w){
Edge[++cnt] = (node){v,w,head[u]};
head[u] = cnt;
}
……
cin >> n ;
for(int i= 1;i< n;i++){
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);add(y,x,w);
}
方法一:两遍dfs求树的直径
1.从任意一个节点出发,通过dfs(bfs)对树进行一遍遍历。找出与出发点距离最远的点x.
2.从x节点出发,再一次通过dfs(bfs)对树进行一遍遍历。找出与出发点距离最远的点y.
x与y点的路径就是树的一条直径。
void dfs(int u){
vis[u] = 1;
if(zjx<d[u]){ //找点x,x里维护最远的点的标号
x= u; zjx = d[u];
}
for(int i = head[u]; i ; i = Edge[i].nxt){
int v = Edge[i].v, w = Edge[i].w;
if(!vis[v]){
d[v] = d[u] + w;
dfs(v);
}
}
……
//主函数里
dfs(1);
memset(d,0,sizeof(d)); memset(vis,false,sizeof(vis));
zjx=0;
dfs(x);
cout << zjx;
方法二:树上dp
设dp[u]表示从节点u出发走向以u为根的子树,能够表达的最远节点的距离。详细阐述以后再图文并茂加吧。
void dpdfs(int u){
vis[u] = 1;
for(int i = head[u]; i ; i = Edge[i].nxt){
int v = Edge[i].v, w = Edge[i].w;
if(!vis[v]){
dpdfs(v);
ans = max(ans , dp[u]+dp[v]+w);
dp[u] = max(dp[u],dp[v]+w);
}
}
}