From e93cb2c0e651241831aa92eb8891cfae9a3666eb Mon Sep 17 00:00:00 2001 From: gameloader Date: Thu, 30 Jan 2025 12:18:23 +0800 Subject: [PATCH] leetcode update --- content/posts/leetcode.md | 118 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index 2d8eced..67038a0 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -22374,3 +22374,121 @@ public: } }; ``` +## day327 2025-01-30 +### 2493. Divide Nodes Into the Maximum Number of Groups +You are given a positive integer n representing the number of nodes in an undirected graph. The nodes are labeled from 1 to n. + +You are also given a 2D integer array edges, where edges[i] = [ai, bi] indicates that there is a bidirectional edge between nodes ai and bi. Notice that the given graph may be disconnected. + +Divide the nodes of the graph into m groups (1-indexed) such that: + +Each node in the graph belongs to exactly one group. +For every pair of nodes in the graph that are connected by an edge [ai, bi], if ai belongs to the group with index x, and bi belongs to the group with index y, then |y - x| = 1. +Return the maximum number of groups (i.e., maximum m) into which you can divide the nodes. Return -1 if it is impossible to group the nodes with the given conditions. + +![0130utjkuecb0rBs](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0130utjkuecb0rBs.png) + +### 题解 +本题是一道难题,也是看了题解才明白如何解决该题。 + +要想解决该题,首先要观察到一个非常重要的现象,即任何可以被分成两个以上不同组的连通图中的所有节点,最终都可以通过合并归类到两个组中。如当前有四个不同的组,则一定可以将第四组和第二组合并到一起,因为二者都和第三组相连,二第四组和第一组又无关,因此合并后一定满足条件,现在变为三组,同理可以将第三组和第一组合并,这样就变成了两组,由此可以发现,只要图能够被分为两组,且只有位于不同组的节点之间有边,该图就可以被分成不同的组,即图必须是一个二分图。 + +判断二分图的常用办法即涂色法,即将图中节点分为两组后,每组中的全部节点都是同一种颜色。在实际实现时,只需将每个节点的相邻节点都涂为和当前节点不同的颜色即可,一旦出现冲突即说明该图不是二分图。 + +第二个难点是观察到是二分图的情况下,一个连通图最多可以被分成的组的个数即为图的直径(图中两个节点之间最远的距离),其实想到了之后验证起来就会觉得这也是一个很显然的事情(会的不难,难的不会,验证想法的难度总小于想到想法本身),只考虑直径这条路径,则这条路径从任意一端开始,执行bfs,将一次bfs中同一层的所有节点都放在同一个组中,如此反复,最终到直径的另一端,一定可以得到一种有效的分组,而直径又是图中的两节点之间的最长距离,因此不可能有数量更多的分组了。无向图的直径的计算最简便直接的方法是对每个节点都执行bfs,找到离该节点最远的节点的距离,所有距离中的最大值即为该图的直径。 + +### 代码 +```cpp +class Solution { +public: + int bfs(int start, vector>& adj, int n) { + vector levels(n + 1, -1); + queue q; + q.push(start); + levels[start] = 0; + int maxLevel = 0; + + while (!q.empty()) { + int curr = q.front(); + q.pop(); + + for (int next : adj[curr]) { + if (levels[next] == -1) { + levels[next] = levels[curr] + 1; + maxLevel = max(maxLevel, levels[next]); + q.push(next); + } + } + } + return maxLevel + 1; // 返回最大可能的分组数 + } + + bool isBipartite(int start, vector>& adj, vector& color, int n) { + queue q; + q.push(start); + color[start] = 0; + + while (!q.empty()) { + int curr = q.front(); + q.pop(); + + for (int next : adj[curr]) { + if (color[next] == -1) { + color[next] = 1 - color[curr]; + q.push(next); + } else if (color[next] == color[curr]) { + return false; + } + } + } + return true; + } + + void dfs(int node, vector>& adj, vector& visited, vector& component) { + visited[node] = true; + component.push_back(node); + + for (int next : adj[node]) { + if (!visited[next]) { + dfs(next, adj, visited, component); + } + } + } + + int magnificentSets(int n, vector>& edges) { + vector> adj(n + 1); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + vector visited(n + 1, false); + vector> components; + + for (int i = 1; i <= n; i++) { + if (!visited[i]) { + vector component; + dfs(i, adj, visited, component); + components.push_back(component); + } + } + + int result = 0; + for (const auto& component : components) { + vector color(n + 1, -1); + if (!isBipartite(component[0], adj, color, n)) { + return -1; + } + + // 对于每个连通分量,尝试从每个节点开始BFS,取最大值 + int maxGroups = 0; + for (int node : component) { + maxGroups = max(maxGroups, bfs(node, adj, n)); + } + result += maxGroups; + } + + return result; + } +}; +```