洛谷 P3174 【[HAOI2009]毛毛虫】

一看这道题立马想到了树的直径

还没有两遍(dfs)求树的直径的做法,赶快来水一发


已经会树的直径的大佬可以跳过这一段(qwq)

树的直径:树上最长的一条链

具体求法有两种,一种是树形(DP),这里就不细讲了,我着重讲下两遍(dfs)求树的直径的做法,可以说很经典了。

做法: 先从任意一点出发,找到离出发点的最远点,然后再从找到的那个点出发,找一次最远点,这两点就是树的直径的两个端点。


以下是证明(也不是必须会证明啦,不想看的(dalao)可以跳过)

(dis)的意思是两点直接的长度,图片中两点之间的长度是不确定的,比如(1)(2)之间可能有多个节点,只是简洁点画


接下来回到这道题,跟树的直径是不是就有点联系了,树的直径的(dfs)的时候每延申(1)个点,我们只需要加上(1)个长度,而这道题应该这样加:

假设我们(dfs)(x)开始,那么我们一开始初始的总和为(dis[x])(dis)为这个点连到的所有点),设(nx)(x)的儿子,那么往(nx)展的时候,应该加上(dis[nx]-2),因为当搜到(nx)的时候,首先他自己这个点,已经被他的父亲(x)加过了,而且(nx)还连了一个(x)(x)被父亲自己加过了,所以不需要再加了,所以减(2)

思路讲完了,下面是代码时间~

#include <bits/stdc++.h>
using namespace std;
int n , m , ans1 , ans2 , maxx , tot , f = 0;	//ans1为第一次的最远点,ans2为第二次的最远点 
int dis[300010];
vector<int> e[300010]; 
stack<int> s;
void dfs1(int x , int sum , int fa){
	if(maxx < sum){
		maxx = sum;
		ans1 = x;
	}
	for(int i = 0; i < e[x].size(); i++){
		int nx = e[x][i];
		if(nx == fa) continue;
		dfs1(nx , sum + dis[nx] - 2 , x);
	}
} 
void dfs2(int x , int sum , int fa){
	if(tot < sum){
		ans2 = x;
		tot = sum;
	}
	for(int i = 0; i < e[x].size(); i++){
		int nx = e[x][i];
		if(nx == fa) continue;
		dfs2(nx , sum + dis[nx] - 2 , x);
	}
}
int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++) dis[i]++;	//自己也算连到了 
	for(int i = 1; i <= m; i++){
		int x , y;
		cin >> x >> y;
		e[x].push_back(y);
		e[y].push_back(x);
		dis[x]++;
		dis[y]++;
	}
	dfs1(1 , dis[1] , 0);
	dfs2(ans1 , dis[ans1] , 0);
	cout << tot;
	return 0;
}
原文地址:https://www.cnblogs.com/bzzs/p/13674669.html