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

一、题目

1、题目描述

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

2、基础框架

  • C语言 版本给出的基础框架代码如下:
int maximumInvitations(int* favorite, int favoriteSize){

}

3、原题链接

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

二、解题报告

1、思路分析

   ( 1 ) (1) (1) 首先问几个问题:是不是一定有环?有没有可能有多个环?如果有多个环,这多个环的人能不能都加上会议?
   ( 2 ) (2) (2) 是不是一定有环?答案是一定有。假设没有环,对于某一条链的末尾结点,指向的一定是链外的结点,这样它就不可能是末尾结点,和假设矛盾。所以它一定指向链内的结点,这样就形成了环。
   ( 3 ) (3) (3) 有没有可能有多个环?有可能。
   ( 4 ) (4) (4) 如果有多个两人以上环,这多个环的人能不能都加上会议?答案是不行,因为参加会议的一定是一个连通图。如果把两个环的人弄进来,就一定不可能是一个连通图了。

   ( 4.1 ) (4.1) (4.1) 所有入度为零的结点,对整个环不产生贡献;
   ( 4.2 ) (4.2) (4.2) 去掉所有入度为零的结点,并且将它指向的结点入度减一,如果那个结点变成了入度为零的点,那么,它对这个环也是没有贡献的。
   ( 4.3 ) (4.3) (4.3) 按照这样的方法,把所有没有贡献的结点去掉,这就是 拓扑排序,可以利用广度优先搜索来实现。

   ( 5 ) (5) (5) 如果有一个二人环,那么分别从两个人去找各自的 “被喜欢人” 的最长链 加和以后就是一个可行解。

   ( 5.1 ) (5.1) (5.1) 对于这种情况,我们可以分别对两个互相喜欢的结点进行向外找最长路(注意,这里要建反向边),对于这两个结点,其实分别是一棵树。于是,问题转变成了求树的从根结点到叶子结点的最长路。

   ( 5.2 ) (5.2) (5.2) 任意的两两喜欢的链,都一定可以放进圆桌会议。

2、时间复杂度

   O ( n ) O(n) O(n)

3、代码详解

class Solution {
    vector <int> edges[100010];
    queue <int> q;
    bool visited[100010];
    int inDegree[100010];
    
    int maxLink(int u, int ban) {
        int i;
        int maxl = 1;
        for(i = 0; i < edges[u].size(); ++i) {
            int v = edges[u][i];
            if(v == ban) {
                continue;
            }
            maxl = max(maxl,  maxLink(v, ban) + 1);
        }
        return maxl;
    }

    void init(int n, vector<int>& favorite) {
        memset(inDegree, 0, sizeof(inDegree));
        memset(visited, 0, sizeof(visited));
        while(!q.empty()) q.pop();
        for(int i = 0; i < n; ++i) {
            edges[i].clear();
        }

        for(int i = 0; i < n; ++i) {
            // a -> b 表示 a 喜欢 b
            int a = i;
            int b = favorite[i];
            ++inDegree[b];           // 建立入度表
            edges[b].push_back(a);   // 建立反向图
        }
    }

public:
    int maximumInvitations(vector<int>& favorite) {
        int i;
        int ans = 0, x;
        int n = favorite.size();
        init(n, favorite);

        // a 和 b 互相喜欢,并且 a 找最长路, b 找最长路,然后相加
        for(i = 0; i < n; ++i) {
            int a = i;
            int b = favorite[i];
            if(favorite[b] == a) {  // 说明 a 和 b 互相喜欢
                x = maxLink(a, b) + maxLink(b, a);
                ans = ans + x;
            }
        }
        ans /= 2;

        for(i = 0; i < n; ++i) {
            if(!inDegree[i]) {
                q.push(i);
                visited[i] = 1;
            }
        }

        // 拓扑排序
        // 因为舔狗不得house,所以我们需要给他一个家,这个家就是我们的队列
        // 于是,最后所有visited[i] = 1对应的i,都是舔狗
        while(!q.empty()) {
            int u = q.front();
            q.pop();

            int v = favorite[u];
            
            if(--inDegree[v] == 0) {
                q.push(v);
                visited[v] = 1;
            }
        }

        for(i = 0; i < n; ++i) {
            if(visited[i]) {
                continue;
            }
            visited[i] = 1;

            int cnt = 1;
            int start = favorite[i];
            while(start != i) {
                visited[start] = 1;
                ++cnt;
                start = favorite[start];
            }
            ans = max(ans, cnt);
        }
    
        return ans;
    }
};

三、本题小知识

  遇到图的问题,要根据图的特殊性(比如这道题的图,每个结点一定有一条出边)来思考对应的解题方案,而不是盲目的去找环。


四、加群须知

  相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」
  那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:

🌌《算法入门指引》🌌

  如果链接被屏蔽,或者有权限问题,可以私聊作者解决。

  大致题集一览:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述



在这里插入图片描述


  为了让这件事情变得有趣,以及「 照顾初学者 」,目前题目只开放最简单的算法 「 枚举系列 」 (包括:线性枚举、双指针、前缀和、二分枚举、三分枚举),当有 一半成员刷完 「 枚举系列 」 的所有题以后,会开放下个章节,等这套题全部刷完,你还在群里,那么你就会成为「 夜深人静写算法 」专家团 的一员。
  不要小看这个专家团,三年之后,你将会是别人 望尘莫及 的存在。如果要加入,可以联系我,考虑到大家都是学生, 没有「 主要经济来源 」,在你成为神的路上,「 不会索取任何 」
  🔥联系作者,或者扫作者主页二维码加群,加入刷题行列吧🔥


🔥让天下没有难学的算法🔥

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

让你养成九天持续刷题的习惯
🔥《九日集训》🔥

入门级C语言真题汇总
🧡《C语言入门100例》🧡

组团学习,抱团生长
🌌《算法零基础100讲》🌌

几张动图学会一种数据结构
🌳《画解数据结构》🌳

竞赛选手金典图文教程
💜《夜深人静写算法》💜
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值