[拓扑排序+基环内向树+动态规划]参加会议的最多员工数

题目描述

一个公司准备组织一场会议,邀请名单上有 n 位员工。公司准备了一张 圆形 的桌子,可以坐下 任意数目 的员工。

员工编号为 0 到 n - 1 。每位员工都有一位 喜欢 的员工,每位员工 当且仅当 他被安排在喜欢员工的旁边,他才会参加会议。每位员工喜欢的员工 不会 是他自己。

给你一个下标从 0 开始的整数数组 favorite ,其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目 。

思路分析

  1. 拓扑排序里利用没有入队的元素——剩余的元素一定形成了环

  2. 绿色的非环内元素,一定最终通过拓扑排序,最终都指向了环内的元素了

  3.  但并不一定所有环上的元素i都是有最大路径f[i]的值的,但对于>2的环实质是用不到的

官方source code

class Solution {
public:
    int maximumInvitations(vector<int>& favorite) {
        int n = favorite.size();
        // 统计入度,便于进行拓扑排序
        vector<int> indeg(n);
        for (int i = 0; i < n; ++i)
            ++indeg[favorite[i]];
        vector<int> used(n), f(n, 1);
        queue<int> q;
        for (int i = 0; i < n; ++i)
            if (!indeg[i])
                q.push(i);
        while (!q.empty()) {
            int u = q.front();
            used[u] = true;
            q.pop();
            int v = favorite[u];
            // 状态转移
            f[v] = max(f[v], f[u] + 1);
            --indeg[v];
            if (!indeg[v]) q.push(v);
        }
        // ring 表示最大的环的大小
        // total 表示所有环大小为 2 的「基环内向树」上的最长的「双向游走」路径之和
        int ring = 0, total = 0;
        for (int i = 0; i < n; ++i)
            if (!used[i]) {
                int j = favorite[i];
                // favorite[favorite[i]] = i 说明环的大小为 2
                if (favorite[j] == i) {
                    total += f[i] + f[j];  //f一开始都是初始化为1的
                    used[j] = true;       //避免重复,缩减时间复杂度
                }
                else { // 否则环的大小至少为 3,我们需要找出环
                    int u = i, cnt = 0;
                    while (true) {
                        ++cnt;
                        u = favorite[u];
                        used[u] = true; //避免重复,缩减时间复杂度
                        if (u == i) break;
                    }
                    ring = max(ring, cnt);
                }
            }
        return max(ring, total);
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值