参加会议的最多员工数
一个公司准备组织一场会议,邀请名单上有 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);
}
};