1. 程式人生 > 其它 >牛客程式設計巔峰賽:經過直徑的點(樹的直徑升級版)

牛客程式設計巔峰賽:經過直徑的點(樹的直徑升級版)

技術標籤:圖論

連結:https://ac.nowcoder.com/acm/contest/9753/C
來源:牛客網

題目描述

牛牛有一棵n個點的無權無根樹,他想知道有多少個點在樹的直徑上,你可以幫幫他嗎?

注意:樹的直徑可能不止一條。

示例1

輸入

3,[1,2],[2,3]

返回值

3

說明

直徑為1−2−3,三個節點均在直徑上,故答案為3。直徑為1-2-3,三個節點均在直徑上,故答案為3。直徑為1−2−3,三個節點均在直徑上,故答案為3。

備註:

2≤n≤1e5

思路:

錯誤想法及程式碼】這道題讓我卡了整整一天,我之前的思路是通過兩次dfs找到樹的直徑長度以及直徑的某一個端點【不懂樹的直徑的求法可以參考我這篇

部落格】。然後從某一個端點出發進行dfs並記錄路徑長度以及之後最多能走多深,如果這兩個值的和等於樹的直徑,則這個點就一定是直徑上的點,具體程式碼如下:

class Solution {
public:
    /**
     * 程式碼中的類名、方法名、引數名已經指定,請勿修改,直接返回方法規定的值即可
     * 
     * @param n int整型 節點個數
     * @param u int整型vector 
     * @param v int整型vector 
     * @return int整型
     */
    int ans,mxR;
    int depth[100005];
    vector<int> edges[100005];
    int dfs1(int u,int f,int now){
        int res=0;
        int size=edges[u].size();
        for(int i=0;i<size;i++){
            int v=edges[u][i];
            if(v==f) continue;
            dfs1(v,u,now+1);
            res=max(res,depth[v]);
        }
        if(res+now==mxR)
            ans++;
        return depth[u]=res+1;
    }
    void dfs(int u,int f){
        depth[u]=depth[f]+1;
        int size=edges[u].size();
        for(int i=0;i<size;i++){
            int v=edges[u][i];
            if(v==f) continue;
            dfs(v,u);
        }
    }
    int PointsOnDiameter(int n, vector<int>& u, vector<int>& v) {
        // write code here
        ans=mxR=0;
        int len=u.size();
        for(int i=0;i<len;i++){
            edges[u[i]].push_back(v[i]);
            edges[v[i]].push_back(u[i]);
        }
        memset(depth, 0, sizeof(depth));
        dfs(1,0);
        int start=1;
        for(int i=1;i<=n;i++)
            if(depth[i]>depth[start])
                start=i;
        dfs(start,0);
        for(int i=1;i<=n;i++)
            mxR=max(mxR,depth[i]);
        memset(depth, 0, sizeof(depth));
        dfs1(start,0,1);
        return ans;
    }
};

然而只能過91%的樣例,卡了一晚上不知道錯在什麼地方【最坑的是發現比賽中許多人竟然用這種方法過了,可能後期增加資料了,但比賽沒有重判!】

睡了個午覺突然發現了一個卡掉我方法的樣例,其示意圖如下:

按道理說所有點的都在直徑上的,然而我的方法一定會遺漏一個點!【這點留給讀者想】

AC】進一步改善程式碼,我們從直徑的一個端點走到直徑的中間,然後從中間為起點進行dfs,並找到所有能走到深度為直徑的二分之一的點,並統計數量即可。

class Solution {
public:
    /**
     * 程式碼中的類名、方法名、引數名已經指定,請勿修改,直接返回方法規定的值即可
     * 
     * @param n int整型 節點個數
     * @param u int整型vector 
     * @param v int整型vector 
     * @return int整型
     */
    int depth[100005];
    int f[100005],g[100005];
    vector<int> edges[100005];
    void dfs(int u,int fa){
        depth[u]=depth[fa]+1;
        f[u]=fa;
        g[u]=depth[u];
        int size=edges[u].size();
        for(int i=0;i<size;i++){
            int v=edges[u][i];
            if(v==fa) continue;
            dfs(v,u);
            g[u]=max(g[u], g[v]);
        }
    }
    int PointsOnDiameter(int n, vector<int>& u, vector<int>& v) {
        // write code here
        int ans=0;
        for(int i=0;i<n-1;i++){
            edges[u[i]].push_back(v[i]);
            edges[v[i]].push_back(u[i]);
        }
        dfs(1,0);
        int start=1;
        for(int i=1;i<=n;i++)
            if(depth[i]>depth[start])
                start=i;
        dfs(start,0);
        for(int i=1;i<=n;i++)
            if(depth[i]>depth[start])
                start=i;
        int d=depth[start];
        if(d%2==0){
            for(int i=1;i<d/2;i++)
                start=f[start];
            depth[f[start]]=0;
            dfs(start,f[start]);
            depth[start]=0;
            dfs(f[start],start);
            for(int i=1;i<=n;i++)
                 if(g[i]==d/2)
                     ans++;
        }
        else{
            for(int i=1;i<=d/2;i++)
                start=f[start];
            dfs(start,0);
            for(int i=1;i<=n;i++)
                if(g[i]==d/2+1)
                    ans++;
        }
        return ans;
    }
};