1. 程式人生 > 實用技巧 >2020年8月17日 面向物件的高階特性

2020年8月17日 面向物件的高階特性

先看題:P1395會議

樹的重心,就是在一棵樹中拆掉一個點,把這棵樹分成幾個部分,使得最大的部分最小,亦即拆得均勻,這個拆掉的點就是樹的重心。

那麼怎麼求呢?我們可以從根結點開始dfs(什麼?你說是無根樹。那就定義節點1為根唄),返回值是這棵子樹的大小。然後定義一個\(mx\)\(mx=max\{dfs(\text{該結點的子結點的})\}\) 。最後,在列舉完子節點後,再判斷他父親那一塊的大小。然後記錄值。
現在唯一的問題就是父親那塊怎麼解決呢?沒錯,用總結點數減去這棵子樹的大小,就是他父親那一塊的大小。

現在來解決第二問 ,這問其實可以在解決樹的重心後直接dfs或bfs,然後就可以得出總的路程了。這一塊就不用詳細解釋了。

上程式碼:

#include<bits/stdc++.h>
using namespace std;
int n,ans=50005,sum=50005,road;//n是總結點數,ans是樹的重心的編號,sum是其最那塊的大小,road是總路程
int t[50005];//ti表示結點i到ans的距離。
queue<int>q;//q用來後面bfs用
struct graph
{
	int tot;
	int dt[100005],nxt[100005];
	int hd[50005];
	void add(int x,int y)
	{
		tot++;
		nxt[tot]=hd[x];
		hd[x]=tot;
		dt[tot]=y;
	}
}g;//graph是鏈式前向星。
int dfs(int x,int fa)
{
	int k=0;//k代表其所以子樹的大小
	int mx=0;//mx是將這個點拆掉後最大那塊的大小
	for(int i=g.hd[x];i;i=g.nxt[i])//遍歷所以點子節點
	 if(g.dt[i]!=fa)//注意判該節點是否是x的父親,不判會爆棧的
	 {
	 	int xx=dfs(g.dt[i],x);//記錄這棵子樹的大小
		k+=xx;//加上這棵子樹的大小
		mx=max(mx,xx);//取最大值
	 }
	mx=max(mx,n-k-1);//最後判一下父親那塊,最後的-1是因為最開始沒有把自己算進去
	if(mx<sum||(mx==sum&&x<ans)) sum=mx,ans=x;//如果比當前方案更優,就取這個方案
	return k+1;//返回整棵子樹的大小
}
void bfs()
{
	q.push(ans);//記錄最初的點
	while(!q.empty())//如果佇列不為空
	{
		int xx=q.front();//記錄隊首
		q.pop();//彈出隊首
		for(int i=g.hd[xx];i;i=g.nxt[i])//遍歷連結它的節點
		{
			int yy=g.dt[i];
			if(t[yy]||yy==ans) continue;//如果這個點已經被遍歷過,continue
			t[yy]=t[xx]+1;//記錄距離
			road+=t[yy];//總距離要加上這個點的距離
			q.push(yy);//放進佇列
		}
	}
	return ;
}
int main()
{
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int from,to;
		cin>>from>>to;
		g.add(from,to);
		g.add(to,from);
	}//輸入+存圖,注意是雙向邊
	dfs(1,0);//dfs,把1的父親設為0,就是沒有父節點了(因為沒有節點0)
	bfs();//求出樹的重心後,求總路程
	cout<<ans<<' '<<road;
	return 0;
}