参加会议的最多员工数

一个公司准备组织一场会议,邀请名单上有 n 位员工。公司准备了一张圆形 的桌子,可以坐下任意数目的员工。
员工编号为 0 到 n - 1 。每位员工都有一位喜欢的员工,每位员工当且仅当他被安排在喜欢员工的旁边,他才会参加会议。
每位员工喜欢的员工不会是他自己。
给你一个下标从 0 开始的整数数组 favorite ,其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目 。

1. 拓扑排序+基环树

class Solution {
public:
    int maximumInvitations(vector<int>& g) {
        //找最大环,和所有二元环的延伸环之和
        int n = g.size();
        vector<vector<int>> rg(n); // 反图
        vector<int> deg(n);
        for (int x = 0; x < n; x++) {//建立反图和入度表
            int y = g[x];
            rg[y].push_back(x);
            deg[y]++;
        }
        // 拓扑排序,剪掉 g 上的所有树枝
        // 拓扑排序后,deg 值为 1 的点必定在基环上,为 0 的点必定在树枝上
        queue<int> q;
        for (int i = 0; i < n; i++) 
            if (deg[i] == 0)  q.push(i);
        
        while (!q.empty()) {
            int x = q.front(); q.pop();
            int y = g[x];
            if (--deg[y] == 0)  q.push(y);
        }

        int res = 0;
        // 在反图上遍历树枝
        int rpath = 0;
        function<void(int, int)> rdfs = [&](int x, int depth) {
            rpath = max(rpath,depth);
            for (int y: rg[x]) {
                if (deg[y] == 0)  // 避免访问基环
                    rdfs(y, depth + 1);
            }
        };

        int doures = 0;//双元环所有长度
        for (int i = 0; i < n; i++) {
            if (deg[i] < 1)  continue;
            vector<int> ring;
            for (int x = i;; x = g[x]) { //遍历基环
                deg[x] = -1; // 将基环上的点的入度标记为 -1,避免重复访问,当做vis使用
                ring.push_back(x); // 收集在基环上的点
                if (g[x] == i)  break;
            }
            res = max(res,(int)ring.size());
            
            
            if(ring.size()==2){
                int curring = 2;
                for (int x: ring){
                    rpath = 0;//重置
                    rdfs(x, 0); 
                    curring += rpath;
                }
                doures+=curring;
            }
        }
        return max(res,doures);
    }
};
posted @ 2023-11-01 15:32  失控D大白兔  阅读(9)  评论(0编辑  收藏  举报