日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

树学

發布時間:2023/12/3 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 树学 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 題目描述
    • 題解1:
    • 代碼:
    • 題解2:
    • 代碼:

傳送

時間限制:C/C++ 2秒,其他語言4秒 空間限制:C/C++ 262144K,其他語言524288K
64bit IO Format:> %lld

題目描述

牛妹有一張連通圖,由n個點和n-1條邊構成,也就是說這是一棵樹,牛妹可以任意選擇一個點為根,根的深度deproot為0,對于任意一個非根的點,我們將他到根節點路徑上的第一個點稱作他的父節點,例如1為根,1-4的;路徑為1-3-5-4時,4的父節點是5,并且滿足對任意非根節點,depi=depfa i+1,整棵樹的價值W= ,即所有點的深度和

牛妹希望這棵樹的W最小,請你告訴她,選擇哪個點可以使W最小
輸入描述:
第一行,一個數,n
接下來n-1行,每行兩個數x,y,代表x-y是樹上的一條邊
輸出描述:
一行,一個數,最小的W
示例1
輸入

4 1 2 1 3 1 4

輸出

3

備注:
對于30%30%的數據,1<= n<=1000
對于100%100%的數據,1<=n <=106

題解1:

樹形dp+換根
用到的幾個函數:
dep[i]:節點i的深度
ant[i]:i的子樹的個數(含本身)
f[x]:以x為根的每個節點深度的和

圖一為以u為根節點
圖二為以v為根節點
從u轉到v 之后,圖二中黃色區域(u和子樹1和子樹2)根節點都加1(因為成為別人的子節點),綠色區域(v和根節點2)根節點減1(因為成為別人的根節點)
那轉換成公式是什么樣的?
f[v]=(f[u]-ant[v])+(n-ant[v]);
怎么理解呢?
第一個括號里,是將圖二的綠色區域根節點減一,因為黃色區域一共ant[v]個節點,這個區域內每個節點都減1,所以整個區域f[u]要減ant[v].
第二個括號就是黃色區域每個節點都加一,那整個區域就加這個區域的節點數,這個區域的節點數=整個區域-綠色區域,所以就是n-ant[v]
我們從1開始dfs,求出每個節點的深度,即dep[]
然后再dfs求出每個點子樹數量,再dfs換成其他根,利用公式求出f來

代碼:

#include<bits/stdc++.h> #define forr(n) for(int i=1;i<=n;i++) typedef long long ll; using namespace std; const int maxn=1e6+3; struct node{int u,v,w,next; }edge[maxn<<1];//鏈式前項星 ll head[maxn<<1];//無向邊,所以乘2 ll dep[maxn];//節點的深度 ll ant[maxn];//節點x的子樹數量(包含本身) ll f[maxn];//以i為根的時候每個點深度的和 ll cnt=0; ll minn=1e7;ll n; void add(ll u,ll v) {edge[++cnt].v=v;edge[cnt].next=head[u];head[u]=cnt;} inline void init(ll n) {forr(n)f[1]+=dep[i];//在dfs1求完每個點深度后,接著求出以1為根的時候每個點深度的和 forr(n)ant[i]=1;//每個節點的子樹一開始都是本身 } ll v=0; void dfs1(ll now,ll fa) {for(ll i=head[now];i;i=edge[i].next){v=edge[i].v;if(v==fa)continue;dep[v]=dep[now]+1;dfs1(v,now);} }//以1為根節點開始,計算出每個節點的深度 void dfs2(ll now,ll fa) {for(ll i=head[now];i;i=edge[i].next){v=edge[i].v;if(v==fa)continue;dfs2(v,now);ant[now]+=ant[v]; } }//求出x節點的子樹數量 void dfs3(ll now,ll fa) {for(ll i=head[now];i;i=edge[i].next){v=edge[i].v;if(v==fa)continue;f[v]=f[now]-ant[v]+(n-ant[v]); dfs3(v,now);} } //從1開始換成其他根,并求出其他根的f值 int main() {cin>>n;for(int i=1;i<n;i++){int u,v;cin>>u>>v;add(u,v);add(v,u);} dfs1(1,0);init(n);//初始化 dfs2(1,0);dfs3(1,0);forr(n){minn=min(minn,f[i]);} cout<<minn;return 0;}

仔細看會發現dfs1與dfs2結構相似,完全可以和在一起寫
或者用vector寫更簡潔

題解2:

我看有很多大佬都用重心的性質來做
樹的重心有一個這樣的性質:在樹中所有點到某點的距離和 當中,到樹的重心的距離和是最小的,如果有多個重心,那他們距離和一樣。
樹中所有點到重心的距離和最小,不就是我們要求的那個值嗎。
先用dfs樹形dp求出重心,再求出重心與每個點的距離進行累加求和

代碼:

#include<bits/stdc++.h> using namespace std;typedef long long ll; const int maxn=1e6+3; int ant[maxn],root[maxn]; int n,cnt; ll res; ll point=maxn;vector<int>edge[maxn]; void dfs1(int v,int p) {ant[v]=0;int maxx=0;for(int i=0;i<edge[v].size();i++){int u=edge[v][i];if(u!=p){dfs1(u,v);ant[v]+=(ant[u]+1);maxx=max(ant[u],maxx);}}maxx=max(n-ant[v]-1,maxx);if(maxx<point){cnt=0;root[++cnt]=v;point=maxx;}else if(maxx==point)root[++cnt]=v; } void dfs2(int v,int p,int dep) {res+=dep;for(int i=0;i<edge[v].size();i++){int u=edge[v][i];if(u!=p)dfs2(u,v,dep+1);} } int main() {scanf("%d",&n);int u,v;for(int i=1;i<n;i++){cin>>u>>v;edge[u].push_back(v);edge[v].push_back(u);}dfs1(1,0);dfs2(root[1],0,0);printf("%lld\n",res);return 0; }

有關樹的重心其他性質,有空專門講講

總結

以上是生活随笔為你收集整理的树学的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。