1. 程式人生 > 實用技巧 >洛谷 P3174 【[HAOI2009]毛毛蟲】

洛谷 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;
}