hdu3534:tree:樹的直徑問題(dp求解)

題目連接

  • 樹形DP專題

題目大意

  • 給出一棵帶邊權的樹;
  • 問1:求出樹的直徑;
  • 問2:有多少對點的距離等於樹的直徑。

題目分析

  • 直徑的定義: 樹上最長的鏈(可能有多條)。
  • 求樹的直徑的方法1:用兩次dfs來完成
    dfs1從根出發,找到最遠的葉子結點 kk
    dfs2以 kk 爲根,出發,找到離他最遠的點 ttkktt 之間的距離就是直徑。
  • 求直徑的做法2:dp的思維來實現
    從根出發,搜索的過程中,同時記錄最長鏈與次長鏈,回溯的時候更新。
  • 本題解抄的就是方法2,而且更巧妙的是,用一個緩存 tmptmp 來記錄可能最長鏈,節省了一丁點的空間。

解題流程

  • 從根,開始搜索:
  • 回溯到 xx 的時候 5 個操作:
    • 1 用 tmptmp 記錄經過 yy 的最長鏈長度:
      tmp=len[y]+e(x,y);tmp=len[y]+e(x,y); 其中 e(x,y)e(x,y)(x,y)(x,y) 之間的邊長;
    • 2 如果 ans<tmp+len[x]ans<tmp+len[x] 則更新 鏈長 ansans點對 sumsum
      ans=tmp+len[x];ans=tmp+len[x];
      sum=nod[x]nod[y];sum=nod[x]*nod[y];
    • 3 如果 ans==tmp+len[x]ans==tmp+len[x] 則更新 點對 sumsum
      sum+=nod[x]nod[y];sum+=nod[x]*nod[y];
    • 4 如果 tmp>len[x]tmp>len[x] 則更新 xx 的參數: len[x]len[x]nod[x]nod[x]
      len[x]=tmp;len[x]=tmp;
      nod[x]=nod[y];nod[x]=nod[y];
    • 5 如果 tmp==len[x]tmp==len[x] 則更新 xx 的參數: nod[x]nod[x]
      nod[x]+=nod[y];nod[x]+=nod[y];
  • 最後答案是: ansanssumsum

參考代碼

//hdu3534-tree
//求直徑與其端點的對數 
#include<bits/stdc++.h>
using namespace std;
const int N=500007;
const int inf=1e9+7;
struct node{
	int nex,to,c;
}e[N];
int n,ans,sum,len[N],nod[N];
int las[N],cnt;
void add(int x,int y,int c){
	cnt++;
	e[cnt].nex=las[x];
	e[cnt].to=y;
	e[cnt].c=c;
	las[x]=cnt;
}
void dfs(int x,int fa){
	len[x]=0;
	nod[x]=1;
	for(int i=las[x];i;i=e[i].nex){
		int y=e[i].to;
		if(y==fa) continue;
		dfs(y,x);
		
		int tmp=e[i].c+len[y]; //求出經過y點的最長鏈 
		
		if(tmp+len[x]>ans) ans=tmp+len[x],sum=nod[x]*nod[y];
		else if(tmp+len[x]==ans) sum+=nod[x]*nod[y];
		
		if(tmp>len[x]) len[x]=tmp,nod[x]=nod[y];
		else if(tmp==len[x]) nod[x]+=nod[y];
	}
}
int main(){
	while(~scanf("%d",&n)){
		memset(las,0,sizeof(las));
		int x,y,c;
		cnt=0;
		for(int i=1;i<n;i++){
			scanf("%d %d %d",&x,&y,&c);
			add(x,y,c); add(y,x,c);
		}
		ans=-inf;
		sum=0;
		dfs(1,0);
		printf("%d %d\n",ans,sum);
	}
	return 0;
} 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章