[Leetcode Weekly Contest]304

链接:LeetCode

[Leetcode]2357. 使数组中所有元素都等于零

给你一个非负整数数组 nums 。在一步操作中,你必须:

  • 选出一个正整数 x ,x 需要小于或等于 nums 中 最小 的 非零 元素。
  • nums 中的每个正整数都减去 x。

返回使 nums 中所有元素都等于 0 需要的 最少 操作数。

对于数组中的元素,如果有n个不同的且不等于0的元素,需要减n次即可。

class Solution {
    public int minimumOperations(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(var num:nums) {
            if(num!=0) set.add(num);
        }
        return set.size();
    }
}

[Leetcode]2358. 分组的最大数量

给你一个正整数数组 grades ,表示大学中一些学生的成绩。你打算将 所有 学生分为一些 有序 的非空分组,其中分组间的顺序满足以下全部条件:

  • 第 i 个分组中的学生总成绩 小于 第 (i + 1) 个分组中的学生总成绩,对所有组均成立(除了最后一组)。
  • 第 i 个分组中的学生总数 小于 第 (i + 1) 个分组中的学生总数,对所有组均成立(除了最后一组)。

返回可以形成的 最大 组数。

因为第i组学生总分数及学生总数均小于第i+1小组,故可以将grades数组排序(实际上不需要排序),第i个分组分得最小的i个数(i从1开始),最后一个分组的元素个数可能大于分组编号;可将问题转化满足 \((1 + x) * x / 2 <= n\)时x的最大值。

class Solution {
    public int maximumGroups(int[] grades) {
        int n = grades.length;
        int lo = 0, hi = grades.length;
        while(lo <= hi) {
            int mid = lo + ((hi-lo) >> 1);
            long result = 1L * mid * (mid+1) / 2;
            if(result <= n) lo = mid+1;
            else hi = mid-1;
        }
        return hi;
    }
}

[Leetcode]2359. 找到离给定两个节点最近的节点

给你一个 n 个节点的 有向图 ,节点编号为 0 到 n - 1 ,每个节点 至多 有一条出边。
有向图用大小为 n 下标从 0 开始的数组 edges 表示,表示节点 i 有一条有向边指向 edges[i] 。如果节点 i 没有出边,那么 edges[i] == -1 。
同时给你两个节点 node1 和 node2 。
请你返回一个从 node1 和 node2 都能到达节点的编号,使节点 node1 和节点 node2 到这个节点的距离 较大值最小化。如果有多个答案,请返回 最小 的节点编号。如果答案不存在,返回 -1 。
注意 edges 可能包含环。

首先看到这个题目,可以想到求出 node1 和 node2 到其它所有点的最短距离,然后遍历可选择的「中间点」。所以现在的问题就是如何求出其它点到起点的最短路径

「最短路径」算法有 最短路径-Dijkstra 和 无权值最短路径算法(BFS)

  • 「Dijkstra」适用于加权有向图,没有负权重边,且无环,一般是求起点到其它所有点的最短路径,也可以改进为求两点的最短路径
  • 「无权值最短路径算法(BFS)」适用于无权有向图,可以有环,一般是求两点的最短路径,也可以改进为求起点到其它所有点的最短路径

对于本题,每条边的权重均为 1,所以可以看作为无权值,我们使用上述的第二种方法「无权值最短路径算法(BFS)」
由于点与点之间最多只有一条边,所以可以简化「无权值最短路径算法(BFS)」。

class Solution {
    public int closestMeetingNode(int[] edges, int node1, int node2) {
        int[] dis1 = getDis(edges, node1);
        int[] dis2 = getDis(edges, node2);
        int res = Integer.MAX_VALUE, node = -1;
        for(int i=0;i<edges.length;++i) {
            int result = Math.max(dis1[i], dis2[i]);
            if(result<res) {
                res = result;
                node = i;
            }
        }
        return node;
    }

    public int[] getDis(int[] edges, int node) {
        int n = edges.length;
        int[] dis = new int[n];
        Arrays.fill(dis, Integer.MAX_VALUE);
        int d = 0;
        while(node != -1) {
            if(dis[node] < d) break;
            dis[node] = d;
            d ++;
            node = edges[node];
        }
        return dis;
    }
}

[Leetcode]2360. 图中的最长环

给你一个 n 个节点的 有向图 ,节点编号为 0 到 n - 1 ,其中每个节点 至多 有一条出边。
图用一个大小为 n 下标从 0 开始的数组 edges 表示,节点 i 到节点 edges[i] 之间有一条有向边。如果节点 i 没有出边,那么 edges[i] == -1 。
请你返回图中的 最长 环,如果没有任何环,请返回 -1 。
一个环指的是起点和终点是 同一个 节点的路径。

拓扑排序。根据构建出的有向图,依次删除入度为 0 的节点,得到图中所有的环。然后采用深度优先(DFS)或广度优先(BFS)遍历,求出每个环的大小。
除了使用拓扑排序外,我们还可以利用时间戳来实现找环的逻辑。
具体来说,初始时间戳 \(\textit{clock}=1\),首次访问一个点 x 时,记录访问这个点的时间 \(\textit{time}[x]=\textit{clock}\),然后将 \(\textit{clock}\) 加一。
如果首次访问一个点,则记录当前时间 \(\textit{startTime}=\textit{clock}\),并尝试从这个点出发,看能否找到环。如果找到了一个之前访问过的点 x,且之前访问 x 的时间不早于 \(\textit{startTime}\),则说明我们找到了一个新的环,此时的环长就是前后两次访问 x 的时间差,即 \(\textit{clock}-\textit{time}\)。取所有环长的最大值作为答案。若没有找到环,则返回 -1。

class Solution {
    public int longestCycle1(int[] edges) {
        int n = edges.length, res = -1;
        int[] times = new int[n];
        int curTime = 1;
        for(int i=0;i<n;++i) {
            if(times[i] > 0) continue;
            int node = i, startTime = curTime;
            while(node != -1) {
                if(times[node] >= startTime) {
                    res = Math.max(res, curTime - times[node]);
                    break;
                }
                else if(times[node] > 0) break;
                times[node] = curTime;
                curTime ++;
                node = edges[node];
            }
        }
        return res;
    }
}

class Solution {
    public int longestCycle2(int[] edges) {
        int n = edges.length;
        int[] indegree = new int[n];
        for(var edge:edges) {
            if(edge != -1)indegree[edge] ++;
        }
        ArrayDeque<Integer> queue = new ArrayDeque<>();
        for(int i=0;i<n;i++) {
            if(indegree[i] == 0) queue.push(i);
        }
        while(!queue.isEmpty()) {
            int node = queue.pop();
            int nxt = edges[node];
            if(nxt!=-1) {
                indegree[nxt] --;
                if(indegree[nxt]==0)queue.push(nxt);
            }
        }

        int res = -1;
        HashSet<Integer> set = new HashSet<>();
        for(int i=0;i<n;++i) {
            if(indegree[i] == 0 || set.contains(i)) continue;
            int node = i, result = 1;
            while(edges[node] != i) {
                set.add(node);
                node = edges[node];
                result ++;
            }
            res = Math.max(res, result);
        }
        return res;

    }
}

参考:LeetCode

posted @ 2022-08-01 21:38  Jamest  阅读(62)  评论(0编辑  收藏  举报