2127. 参加会议的最多员工数

题目描述

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

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

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

做题情况

  1. 做出来且思路与标答一致
  2. 做出来但思路较为复杂
  3. 有思路,但时间复杂度较高无法通过
  4. 没有思路 ☑

自己的想法:

可能因为自己的状态没有很好,所以对这道题没有什么想法。不过这种算法确实是自己第一次接触,很难全部想清楚。
这其实用到了一种新的数据结构,也不能说是数据结构吧,就是一种有名字的结构,而且这种结构和这道题完美的契合,这就蛮有趣的了。

标答:基环内向树+拓扑排序

首先介绍一下基环内向树。
顾名思义,一棵树有一个内环,而且是向内的,即向环的。这种结构就很像之前的用柳枝编成的花环。
这个结构与这道题完美契合:每个点有且仅有一个非自身的前驱节点,因此我们就根据这个指向来构造树,最终一定会走入到一个环中。(因为n个节点就会有n个前驱节点,且所有的前驱节点均为这n个点。我们沿着前驱节点的方向可以一直走,即一定会走入到一个环中去)。
可以预想到,所有的节点分成了很多堆,每一堆都有一个环。显然的,让一个环围着桌子坐显然是一种可行解。
但如果这个环只有两个节点,这两个节点相邻坐,就已经满足和喜欢的员工相邻的条件了。这种情况下,我们可以拓展枝叶,即从这个二元环向外延申,延申出的枝叶同样可以沿着桌子坐。
如何求延申出枝叶的最长长度,就需要用到拓扑排序。
我们用一个队列来表示当前没有前驱节点的节点,那么他的最长长度就是前驱节点中最长的长度。
(这个就不多讲了,感觉自己掌握的还不错)
还有一个细节,就是可以扩展多个只有两个点的环,包括环两个点延申出的枝叶。所以解分为两种,一种是多节点环,这个只能有一个环,一种是二元环,这个可以很多个二元环一起,包括枝叶。

实际代码

class Solution {
public:
    int maximumInvitations(vector<int>& favorite)
    {
        //前面的排序可以用一个queue来进行实现
        //剩下的就是在环上的,可以用一个set来实现,每次取第一个
        int n = favorite.size();
        vector<vector<int>> connect(n); //前驱节点
        for (int i = 0; i < n; ++i) connect[favorite[i]].push_back(i);
        vector<int> last_num(n); //变化量,前驱节点有几个
        vector<int> length(n, -1); //前驱节点个数
        queue<int> que;
        for (int i = 0; i < n; ++i)
        {
            last_num[i] = connect[i].size();
            if (last_num[i] == 0) que.push(i);
        }
        unordered_set<int> un_set;
        for (int i = 0; i < n; ++i) un_set.insert(i);
        while (!que.empty())
        {
            int top = que.front();
            que.pop();
            un_set.erase(top);
            if (connect[top].size() == 0) length[top] = 1;
            else
            {
                int len = 0;
                for (int i = 0; i < connect[top].size(); ++i)
                {
                    if (len < length[connect[top][i]]) len = length[connect[top][i]];
                    
                }
                length[top] = len + 1;
            }
            last_num[favorite[top]]--;
            if (last_num[favorite[top]] == 0) que.push(favorite[top]);
        }
        int res = 0;
        int lennum2 = 0;
        while (un_set.size())
        {
            auto iter = un_set.begin();
            int first = *iter;
            int it = favorite[first];
            int len = 1;
            un_set.erase(it);
            while (it != first)
            {
                it = favorite[it];
                un_set.erase(it);
                len++;
            }
            if (len == 2)
            {
                int len1 = 0;
                for (int i = 0; i < connect[first].size(); ++i)
                {
                    if (length[connect[first][i]] > len1) len1 = length[connect[first][i]];
                }
                int len2 = 0;
                for (int i = 0; i < connect[favorite[first]].size(); ++i)
                {
                    if (length[connect[favorite[first]][i]] > len2) len2 = length[connect[favorite[first]][i]];
                }
                lennum2 += (2 + len1 + len2);
            }
            else
            {
                if (len > res) res = len;
            }
        }
        return max(res, lennum2);
    }
};

总结

这道题主要就是对于基环向内树的学习了。感觉这种数据结构,知道就是知道,不知道就是不知道,特征鲜明,应用场景比较局限。这次见到了,之后应该就不会不懂了。
希望之后可以灵活应用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值