leetcode 274周赛第四题参加会议的最多员工数

leetcode 274周赛第四题参加会议的最多员工数

题意:

给你一个下标从\(0\) ~ \(n-1\)\(favourite\)数组,\(favourite[i]\)表示下标\(i\)喜欢的人的下标为\(favourite[i]\),现在有张圆形的桌子让你安排这些尽量多的人去参加会议,要保证,每个人的 左右两边至少有一个是自己喜欢的人,每个人只会喜欢一个人,问最多能安排多少人参加会议。

思路:

本题可以转化为图论问题(有向图),假设一些人的喜欢的 人能形成一个环,环上的每一个人都能满足条件——旁边有至少有一个人是自己喜欢的。所以我们可以先求出图中最大环,答案可能是最大环上点的数目\(cnt\)。还有一种情况:假设两个人相互喜欢,那么可以去寻找这两个人的喜欢者的最长的链,形成的一个二元环加上两条链也能满足条件,而且多个这种形式放在一起也能满足条件,所以我们还需要统计出二元环加上两条链的所有情况的参与人数\(ans\),答案就是\(max(ans, cnt)\)

考察算法:

最大环如何求:先用拓扑排序的思想筛选出能形成环的点,在\(dfs\)求出最大的环。

两条最长的链怎么求:可以用\(dfs\)深搜求,这样需要反向建图,这种情况是一定不会搜到环,因为反向建图,指向这个人的是喜欢者,每个喜欢者只会喜欢一个人。

代码:

const int N = 100010;
class Solution {
public:
    int du[N], h[N], e[N], ne[N], idx = 0, vis[N], h1[N], e1[N], ne1[N], idx1 = 0;
    bool st[N];
    int dfs(int index, vector<int>&favorite)//搜最大环
    {
        if(!vis[index])
        {
            vis[index] = true;
            return dfs(favorite[index], favorite) + 1;
        }
        return 0;
    }

    void add(int a,int b)//正向建图
    { 
        e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
    }

    void add1(int a,int b)//反向建图
    { 
        e1[idx1] = b, ne1[idx1] = h1[a], h1[a] = idx1 ++;
    }

    int dfs1(int x)//计算最长的链
    {
        int v = 0;
        for(int kk = h1[x]; kk != -1; kk = ne1[kk]) v = max(v, dfs1(kk));
        return v + 1;
    }
    int calc(int i, int j)//计算两条链的最大长度
    {
        int v = 0;
        for(int k = h1[i]; k != -1; k = ne1[k])
        {
            int x = e1[k];
            if(x != j)
            v = max(v, dfs1(x));
        }
        int w = 0;
        for(int k = h1[j]; k != -1; k = ne1[k])
        {
            int x = e1[k];
            if(x != i)
            w = max(w, dfs1(x));
        }
        return w + v;
    }

    int maximumInvitations(vector<int>& favorite) {
        int n = favorite.size();
        for(int i = 0; i < n; i ++ )
        {
            h[i] = -1;
            h1[i] = -1;
            st[i] = true;
            vis[i] = false;
        }
        for(int i = 0; i < n; i ++ )
        {
            add(i, favorite[i]);
            add1(favorite[i], i);
            du[favorite[i]] ++;
        }
        queue<int>q;
        for(int i = 0; i < n; i ++ )
            if(!du[i])
                q.push(i);
        while(!q.empty())//拓扑排序筛出能形成环的点
        {
            int x = q.front();
            st[x] = false;
            q.pop();
            for(int i = h[x]; i != -1; i = ne[i])
            {
                int y = e[i];
                du[y] --;
                if(!du[y]) q.push(y);
            }
        }
        int cnt = 0;//环的最大长度
        for(int i = 0; i < n; i ++ ){
            if(!vis[i] && st[i])
                cnt = max(dfs(i, favorite), cnt);
        }
        //最长的一个环的长度为cnt。
        //接下来要求二元环产生的可行性方案
        int ans = 0;
        for(int i = 0; i < n; i ++ )
        {
            if(favorite[favorite[i]] == i && favorite[i] > i)//喜欢的人喜欢的人是我自己
            {
                ans += 2 + calc(i, favorite[i]);
            }
        }
        return max(ans, cnt);
    }
};
posted @ 2022-01-02 19:04  合肥学院王星力  阅读(47)  评论(0)    收藏  举报