LeetCode 2127 - Maximum Employees to Be Invited to a Meeting 解題心得( 題目 )

解題概念:DFS、圖論

解題方法:這題也是個圖論題,但是其實沒有很容易看出來,我們把員工A喜歡B的關係視為一個A->B的邊,所以可以把題目想成:給你一個有n點n邊的有向圖,請你找出最大環?

我們先討論找最大環怎麼找,假設題目分成很多子圖(因為圖有可能不是連通的),我們可以發現他的邊個數跟節點數其實一定會相同,透過這個性質:代表這個圖不可能出現兩個以上的環。

這樣就好辦了,我們把目前DFS的歷程加入一個Stack,並且以一個布林陣列紀錄每個點是否已在stack中,當我們DFS到重覆元素即可往回算(利用pop檢查)得到環的大小。

但是注意這題有特例,答案真的是找最大環嗎?考慮一種案例,1和2、3和4都各別互相喜歡,根據上面的想法最大環是2,但正確答案應該是把他們分別擺一起,應該是4。所以我們考慮一種情形:如果這個子圖裡有兩人互相喜歡,也就是有大小為2的環(在此稱為小環),則這些有小環的解可以相加,並且對於小環上的兩個點做倒過來的DFS(可以把此結構視為樹),把"找需要的人"變成"找被需要的",如果有很多就找最大的那個樹。

最後回到問題,答案就是"小環的加總"和"最大環"取最大值。

Java參考程式碼(變數有點多,下方有說明):

//Date:2022/1/4

//Author:En Chi Tsung(欉恩祁)

class Solution {

    int cnt,max,n,c,p,beg;

    boolean countLoop;

    int[] fav=new int[100005];

    boolean[] vis=new boolean[100005];

    boolean[] ins=new boolean[100005];

    Stack<Integer> s=new Stack<>();

    ArrayList<ArrayList<Integer>> rev=new ArrayList<>();

    public int maximumInvitations(int[] favorite) {

        init();

        n=favorite.length;

        for(int i=0;i<n;i++) {

            fav[i]=favorite[i];

            rev.get(fav[i]).add(i);

        }

        for(int i=0;i<n;i++){

            if(vis[i])continue;

            if(i==fav[fav[i]]) { //have found small loop

                Small_Loop(i,fav[i]);

            }

        }

        for(int i=0;i<n;i++){

            if(vis[i])continue;

            s.clear();

            dfs(i);//to find big loop

            if(c>max)max=c;

        }

        return Math.max(cnt,max);

    }

    

    public void Small_Loop(int x,int y) {

        cnt+=(revdfs(x)+revdfs(y));

    }

    

    public int revdfs(int x){ //反邊dfs

        int mx=0;

        if(vis[x])return 0;

        vis[x]=true;

        for(int i:rev.get(x)) {

            if(i==fav[fav[i]])continue;//if it is small loop itself

            mx=Math.max(revdfs(i),mx);

        }

        return mx+1;

    }

    

    public void dfs(int x) {

        if(!ins[x]&&vis[x])return;

        vis[x]=true;

        if(ins[x]) {  //in stack

            c=0;

            beg=x;

            countLoop=true;//start to count

            return;

        }

        ins[x]=true;

        s.push(x);

        dfs(fav[x]);

        p=s.pop();

        if(countLoop) {

            c++;

            if(p==beg) {

                countLoop=false;//count end

            }

        }

        ins[x]=false;

    }

    

    public void init() {

        cnt=0;//initialize

        max=0;

        countLoop=false;

        s.clear();

        rev.clear();

        Arrays.fill(vis,false);

        Arrays.fill(ins,false);

        for(int i=0;i<100005;i++)rev.add(new ArrayList<Integer>());

    }

}

變數說明:

cnt 以小環為解的答案

max 以大環為解的答案

c 算該大環大小計數器

beg 紀錄大環的起點(我們會pop一圈,用beg即可檢查是否算完一圈)

countLoop 如果他為true則計算加一(代表目前元素在環上)

fav[i] i的喜歡對象(正方向的邊)

vis[i] 是否拜訪i

ins[i] i是否重複拜訪(in stack)

rev(型別ArrayList) 記反邊

s(型別Stack) 紀錄拜訪歷程

 

 

 

 

文章標籤
全站熱搜
創作者介紹
創作者 En Chi Tsung 的頭像
En Chi Tsung

阿祁的部落格

En Chi Tsung 發表在 痞客邦 留言(0) 人氣(33)