问题描述 小明的公司要进行集体活动,需要将公司内的 n 个人分组,每个人只能属于一个组。每个人有两个属性:能力值 a i 和性格值 b i 。两个人的差别值定义为能力值的差与性格值的差之和,即 ∣ ∣ + ∣ ∣ ∣a i −a j ∣+∣b i −b j ∣。 分组时要求有至少k 个非空小组,并且如果两个人的差别值不超过某个差别上限 L,那么他们必须在同一个组内。现在我们需要确定在满足至少 k 个组的前提下,差别上限 L 的最大值是多少。 例如:给定 3 3 个人,能力值分别为 [1, 9, 3],性格值分别为 [2, 7, 8],我们可以将第 2 2 人和第 3 3 人分在一组,第一人单独一组,在这种情况下差别上限 L 的最大值为 7 7。 测试样例 样例1: 输入:n = 3 ,k = 2,a = [1, 9, 3],b = [2, 7, 8] 输出:7 样例2: 输入:n = 4 ,k = 3,a = [10, 20, 30, 40],b = [5, 15, 25, 35] 输出:19 样例3: 输入:n = 5 ,k = 2,a = [100, 50, 25, 75, 10],b = [90, 45, 20, 80, 15] 输出:59 问题分析 输入: n: 人数 k: 至少需要的组数 a: 每个人的能力值数组 b: 每个人的性格值数组 输出: 差别上限 L 的最大值,使得在满足至少 k 个组的前提下,所有差别值不超过 L 的人在同一个组内。 解题思路 二分查找: 由于我们需要找到最大的 L,可以考虑使用二分查找来优化查找过程。 设定 L 的范围为 [0, max_difference],其中 max_difference 是所有可能的差别值的最大值。 并查集 (Union-Find): 使用并查集来判断在给定的 L 下,是否可以形成至少 k 个组。 对于每对 (i, j),如果 |a[i] - a[j]| + |b[i] - b[j]| <= L,则将 i 和 j 合并到同一个组。 判断组数: 在每次二分查找的中间值 mid 下,使用并查集判断是否可以形成至少 k 个组。 如果可以,尝试更大的 L;否则,尝试更小的 L。 代码提示 java public class Main { public static int solution(int n, int k, int[] a, int[] b) { // 计算所有可能的差别值 的最大值 int maxDifference = 0; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j ++) { maxDifferenc e = Math.max (maxDifferen ce, Math.abs (a[i] - a [j]) + Math. abs(b[i] - b [j])); } } // 二分查找差别上限 L int left = 0, right = maxDifference; while (left < right) { int mid = left + (right - left + 1) / 2; if (canFormGroups (n, k, a, b, mid)) { left = mid; } else { right = mid - 1; } } return left; } // 判断在给定的 L 下是否可 以形成至少 k 个组 private static boolean canFormGroups(int n, int k, int[] a, int[] b, int L) { // 初始化并查集 int[] parent = new int[n]; for (int i = 0; i < n; i++) { parent[i] = i; } // 合并满足条件的节点 for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j ++) { if (Math.abs (a[i] - a [j]) + Math. abs(b[i] - b [j]) <= L) { union (parent, i, j); } } } // 统计组数 int groupCount = 0; for (int i = 0; i < n; i++) { if (parent[i] == i) { groupCount ++; } } return groupCount >= k; } // 并查集的合并操作 private static void union(int[] parent, int i, int j) { int rootI = find (parent, i); int rootJ = find (parent, j); if (rootI != rootJ) { parent[rootI] = rootJ; } } // 并查集的查找操作 private static int find (int[] parent, int i) { if (parent[i] != i) { parent[i] = find (parent, parent [i]); } return parent[i]; } public static void main (String[] args) { System.out.println (solution(3, 2, new int[]{1, 9, 3}, new int[]{2, 7, 8}) == 7); System.out.println (solution(4, 3, new int[]{10, 20, 30, 40}, new int[]{5, 15, 25, 35}) == 19); System.out.println (solution(5, 2, new int[]{100, 50, 25, 75, 10}, new int[] {90, 45, 20, 80, 15}) == 59); }} 关键步骤解释 计算最大差别值: 通过遍历所有可能的 (i, j) 对,计算出所有可能的差别值的最大值。 二分查找: 使用二分查找在 [0, maxDifference] 范围内查找最大的 L。 并查集: 在 canFormGroups 方法中,使用并查集来判断在给定的 L 下是否可以形成至少 k 个组。 合并与查找操作: union 方法用于合并两个节点,find 方法用于查找节点的根节点。