【Leetcode】周赛304

6134. 找到离给定两个节点最近的节点

给你一个 n 个节点的 有向图 ,节点编号为 0n - 1 ,每个节点 至多 有一条出边。

有向图用大小为 n 下标从 0 开始的数组 edges 表示,表示节点 i 有一条有向边指向 edges[i] 。如果节点 i 没有出边,那么 edges[i] == -1

同时给你两个节点 node1node2

请你返回一个从 node1node2 都能到达节点的编号,使节点 node1 和节点 node2 到这个节点的距离 较大值最小化。如果有多个答案,请返回 最小 的节点编号。如果答案不存在,返回 -1

注意 edges 可能包含环。

示例 1:

img

输入:edges = [2,2,3,-1], node1 = 0, node2 = 1
输出:2
解释:从节点 0 到节点 2 的距离为 1 ,从节点 1 到节点 2 的距离为 1 。
两个距离的较大值为 1 。我们无法得到一个比 1 更小的较大值,所以我们返回节点 2 。

示例 2:

img

输入:edges = [1,2,-1], node1 = 0, node2 = 2
输出:2
解释:节点 0 到节点 2 的距离为 2 ,节点 2 到它自己的距离为 0 。
两个距离的较大值为 2 。我们无法得到一个比 2 更小的较大值,所以我们返回节点 2 。

提示:

  • n == edges.length
  • 2 <= n <= 105
  • -1 <= edges[i] < n
  • edges[i] != i
  • 0 <= node1, node2 < n
我的想法

每个节点 至多 有一条出边→路径唯一有点链表的感觉

能不能按照交叉链表那样做

因为存在环,搞了半天也没整出来

图算法这块还是不行,菜就多练!

正确思路

可以想到求出 node1node2 到其它所有点的最短距离,然后遍历可选择的「中间点」。所以现在的问题就是如何求出其它点到起点的最短路径

为什么想不到遍历!!!

class Solution {
public:
    int closestMeetingNode(vector<int>& edges, int node1, int node2) {
        int n=edges.size();
        auto cal_dis=[&](int s)->vector<int>
        {
            vector<int> dis(n,-1);
            int count=0;
            for(int i=0;i<n;i++)
            {
                if(s==-1)
                    break;
                if(dis[s]!=-1)
                    break;
                dis[s]=count++;
                s=edges[s];
            }
            return dis;
        };

        vector<int> dis1=cal_dis(node1),dis2=cal_dis(node2);
        int ans=-1,min=2147483647;
        for(int i=0;i<n;i++)
        {
            if(dis1[i]==-1||dis2[i]==-1)
                continue;
            if(max(dis1[i],dis2[i])<min)
            {
                ans=i;
                min=max(dis1[i],dis2[i]);
            }
        }
        return ans;
    }
};

6135. 图中的最长环

给你一个 n 个节点的 有向图 ,节点编号为 0n - 1 ,其中每个节点 至多 有一条出边。

图用一个大小为 n 下标从 0 开始的数组 edges 表示,节点 i 到节点 edges[i] 之间有一条有向边。如果节点 i 没有出边,那么 edges[i] == -1

请你返回图中的 最长 环,如果没有任何环,请返回 -1

一个环指的是起点和终点是 同一个 节点的路径。

示例 1:

img

输入:edges = [3,3,4,2,3]
输出去:3
解释:图中的最长环是:2 -> 4 -> 3 -> 2 。
这个环的长度为 3 ,所以返回 3 。

示例 2:

img

输入:edges = [2,-1,3,1]
输出:-1
解释:图中没有任何环。

提示:

  • n == edges.length
  • 2 <= n <= 105
  • -1 <= edges[i] < n
  • edges[i] != i
我的想法

内向基环树找环 + 利用时间戳简单实现

出度至多为1保证连通片只有两种形式

遍历起点在这两种形式里一共有三种情况

6F70E08A4679C41CED7909B06FA1B1F6

class Solution {
public:
    int longestCycle(vector<int>& edges) {
        int n=edges.size();
        int ans=-1;
        vector<int> visit(n,0);//时间戳数组
        int count=0;
        for(int i=0;i<n;i++)
        {
            if(visit[i])
                continue;
            int s=i;
            int init_count=count;
            do
            {
                visit[s]=count;
                s=edges[s];
                count++;
            }while(s!=-1&&!visit[s]);

            if(s==-1)//情况1
                continue;
            if(visit[s]<init_count)//当碰到之前已经遍历过的点,不需要重复遍历,直接跳过
                continue;
            if(visit[s])
                ans=max(count-visit[s],ans);

        }
        return ans;
    }
};
官解

拓扑排序,分离出环

class Solution {
public:
    int longestCycle(vector<int>& edges) {
        int n=edges.size();
        vector<int> indeg(n,0);
        vector<bool> visit(n,false);
        
        //计算入度
        for(int i=0;i<n;i++)
        {
            if(edges[i]!=-1) indeg[edges[i]]++;
        }

        //拓扑排序
        queue<int> q;
        for(int i=0;i<n;i++)
        {
            if(!indeg[i])
            {
                q.push(i);
                visit[i]=true;
            }
        }
        while(!q.empty())
        {
            int v=q.front();
            q.pop();
            int w=edges[v];
            if(w!=-1&&--indeg[w]==0)
            {
                visit[w]=true;
                q.push(w);
            }
        }

        //没有visit到的是环
        int ans=-1;
        for(int i=0;i<n;i++)
        {
            if(!visit[i])
            {
                int count=0;
                int s=i;
                do
                {
                    visit[s]=true;
                    count++;
                    s=edges[s];
                }while(s!=i);

                if(count>ans)
                    ans=count;
            }
        }
        return ans;
    }
};

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

难度困难59

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

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

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

示例 1:

img

输入:favorite = [2,2,1,2]
输出:3
解释:
上图展示了公司邀请员工 0,1 和 2 参加会议以及他们在圆桌上的座位。
没办法邀请所有员工参与会议,因为员工 2 没办法同时坐在 0,1 和 3 员工的旁边。
注意,公司也可以邀请员工 1,2 和 3 参加会议。
所以最多参加会议的员工数目为 3 。

示例 2:

输入:favorite = [1,2,0]
输出:3
解释:
每个员工都至少是另一个员工喜欢的员工。所以公司邀请他们所有人参加会议的前提是所有人都参加了会议。
座位安排同图 1 所示:
- 员工 0 坐在员工 2 和 1 之间。
- 员工 1 坐在员工 0 和 2 之间。
- 员工 2 坐在员工 1 和 0 之间。
参与会议的最多员工数目为 3 。

示例 3:

img

输入:favorite = [3,0,1,4,1]
输出:4
解释:
上图展示了公司可以邀请员工 0,1,3 和 4 参加会议以及他们在圆桌上的座位。
员工 2 无法参加,因为他喜欢的员工 0 旁边的座位已经被占领了。
所以公司只能不邀请员工 2 。
参加会议的最多员工数目为 4 。

提示:

  • n == favorite.length
  • 2 <= n <= 105
  • 0 <= favorite[i] <= n - 1
  • favorite[i] != i
题解
class Solution {
public:
    int maximumInvitations(vector<int>& favorite) {
        int n=favorite.size();
        vector<int> indeg(n,0);
        vector<bool> visit(n,false);
        vector<int> depth(n,1);
        //计算入度
        for(int i=0;i<n;i++)
        {
            indeg[favorite[i]]++;
        }

        //拓扑排序
        queue<int> q;
        for(int i=0;i<n;i++)
        {
            if(!indeg[i])
            {
                q.push(i);
                visit[i]=true;
                depth[i]=1;//
            }
        }
        while(!q.empty())
        {
            int v=q.front();
            q.pop();
            int w=favorite[v];
            depth[w]=max(depth[w],depth[v]+1);//
            if(--indeg[w]==0)
            {
                visit[w]=true;
                q.push(w);
            }
        }
        int ans=1,total=0;
        for(int i=0;i<n;i++)
        {
            if(!visit[i])
            {
                visit[i]=true;
                int j=favorite[i];
                if(favorite[j]==i)
                {
                    visit[j]=true;
                    total+=depth[i]+depth[j];
                }
                else
                {
                    int count=1;
                    while(j!=i)
                    {
                        visit[j]=true;
                        count++;
                        j=favorite[j];
                    }
                    ans=max(ans,count);
                }
            }
        }
        return max(ans,total);
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿离离离离离李

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

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

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

打赏作者

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

抵扣说明:

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

余额充值