dfs 樹的各種搜索

一、查找樹根

樹根的入度爲0。輸入數據時記錄即可。

一本通: 1336:【例3-1】找樹根和孩子  

二、DFS序

void dfs(int x){

       a[++m]=x;  //a數組存儲dfs序
       v[x]=1;   //記錄點x被訪問過
       for  (int i=h[x]; i!=-1; i=ne[i]){
             int y=ne[i];
             if (v[y]) continue;
             dfs(y); 
       }
       a[++m]=x;
}

dfs序的特點:每個節點 x 的編號在序列中恰好出現再次。設這兩次出現的位置爲L[x]與R[x],那麼閉區間 【 L[x], R[x] 】就是以x爲根的子樹的dfs序。 這使我們在很多與樹相關的問題中,可以通過dfs 序把子樹統計轉化爲序列上的區間統計。


三、樹的深度

一本通: 1338:【例3-3】醫院設置

樹中各個節點的深度是一種自頂向下的統計信息。起初,我們已知節點的深度爲0 。若節點 x 的深度爲 d[x] , 則它的子節點 y 的深度就是 d[y] = d[x] +1。在深度優先遍歷的過程中結合自頂向下的遞推,就可以求出每個節點的深度d 。

void dfs(int x) {
	v[x]=1;
	for (int i=h[x]; i!=-1; i=ne[i]) {
		int y=ne[i];
		if (v[y]) continue;
		d[y]=d[x]+1; //從父節點 x到子節點y遞推,計算深度
		dfs(y) ;
	}
}

四、樹的重心

acwing 樹的重心

給定一顆樹,樹中包含n個結點(編號1~n)和n-1條無向邊。

請你找到樹的重心,並輸出將重心刪除後,剩餘各個連通塊中點數的最大值。

重心定義:重心是指樹中的一個結點,如果將這個點刪除後,剩餘各個連通塊中點數的最大值最小,那麼這個節點被稱爲樹的重心。

輸入格式

第一行包含整數n,表示樹的結點數。

接下來n-1行,每行包含兩個整數a和b,表示點a和點b之間存在一條邊。

輸出格式

輸出一個整數m,表示重心的所有的子樹中最大的子樹的結點數目。

數據範圍

1 ≤ n ≤ 10^{5}

輸入樣例

9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6

輸出樣例:

4
#include <bits/stdc++.h>
using namespace std;
const int N=100000+10;
//因爲是雙向邊
int h[N],e[2*N],ne[2*N],idx,ans=N;
bool b[N];//每個結點的訪問標記
int n;
int sum[2*N];

//加入a到b的一條邊
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
 
void dfs(int u){
    b[u]=true;//標記u這個點被搜過
    //size是表示將u點去除後,剩下的子樹中數量的最大值;
    //sum表示以u爲根的子樹的點的多少,初值爲1,因爲已經有了u這個點
    int size=0;
    sum[u]=1;
    for (int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if (!b[j]){
            dfs(j); //s是以j爲根節點的子樹中點的數量
            size=max(size,sum[j]);
            sum[u]=sum[u]+sum[j];
        }
    }
    //n-sum表示的是減掉u爲根的子樹,整個樹剩下的點的數量
    size=max(size,n-sum[u]);
    ans=min(ans,size);
}
 
int main(){
    cin>>n;
    memset(h,-1,sizeof(h));
    for (int i=0; i<n-1; i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    
    dfs(1);
    cout<<ans<<endl;
    
    return 0;
}

五、圖的連通性劃分

通過多次的深度優先遍歷,可以劃分出一張無向圖中的各個連通塊。同理,對一個森林進行深度優先遍歷,可以劃分出森林中的每顆樹。如下面的代碼所示,cnt就是無向圖包含的連通塊的個數,v數組標記了每個點屬於哪一個連通塊。

void dfs(int x) {
	v[x]=cnt;
	for (int i=h[x]; i; i=ne[i]) {
		int y=ne[i];
		if (v[y]) continue;
		dfs(y);
	}
}

for (int i=1; i<=n; i+) {
	if (!v[i]) {
		cnt++;
		dfs(i);
	}
}

六、求樹的直徑

樹的直徑概念:一顆樹上存在的最長路徑。樹中距離最遠的兩個結點之間相隔的距離

方法:兩次dfs       

          先從任意一點P出發,找離它最遠的點Q,再從點Q出發,找離它最遠的點W,W到Q的距離就是樹的直徑

(另外 一種方法是DP)

-----------------------------------------------------------------------------------------------------------

證明如下:

①若P已經在直徑上,根據樹的直徑的定義可知Q也在直徑上且爲直徑的一個端點

②若P不在直徑上,我們用反證法,假設此時WQ不是直徑,AB是直徑

--->若AB與PQ有交點C,由於P到Q最遠,那麼PC+CQ>PC+CA,所以CQ>CA,易得CQ+CB>CA+CB,即CQ+CB>AB,與AB是直徑矛盾,不成立,如下圖(其中AB,PQ不一定是直線,畫成直線是爲了方便):

--->若AB與PQ沒有交點,M爲AB上任意一點,N爲PQ上任意一點。首先還是NP+NQ>NQ+MN+MB,同時減掉NQ,得NP>MN+MB,易知NP+MN>MB,所NP+MN+MA>MB+MA,

即NP+MN+MA>AB,與AB是直徑矛盾,所以這種情況也不成立,如下圖:

//轉自網絡
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100000
using namespace std;
inline int read() 
{
    int x=0;
    bool f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=0;
    for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    if(f) return x;
    return 0-x;
}
struct node
{
    int u,v,w,nex;
}edge[2*maxn+10];
int n,m,d[maxn+10],head[maxn+10],f_num,cnt=0,ans;
inline void add(int x,int y,int z)
{
    cnt++;
    edge[cnt].u=x;
    edge[cnt].v=y;
    edge[cnt].w=z;
    edge[cnt].nex=head[x];
    head[x]=cnt;
}
inline void dfs(int x,int fa)
{
    if(ans<d[x])
    {
        ans=d[x];
        f_num=x;
    }
    for(int i=head[x];i!=-1;i=edge[i].nex)
    {
        int j=edge[i].v;
        if(j==fa)continue;
        d[j]=d[x]+edge[i].w;    
        dfs(j,x);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0);
    ans=0;
    d[f_num]=0;
    dfs(f_num,0);
    printf("%d",ans);
    return 0;
}

七、dfs的樹和圖搜索的基本代碼

void dfs(int x){
	v[x]=1;
	for (int i=h[x]; ~i; i=ne[i]){  //i爲-1時取反爲0, 非-1的i取反都不爲0
		int y=ne[i];
		if (v[y]) continue;
		dfs(y);
	}
}

 

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