Hard

题目描述

给你一个二维整数数组 points,其中 points[i] = [xi, yi] 表示笛卡尔平面上第 i 个点的坐标。

两点 points[i] = [xi, yi]points[j] = [xj, yj] 之间的曼哈顿距离为 |xi - xj| + |yi - yj|

n 个点分割成恰好两个非空组。分割的分割因子是同一组内所有无序点对之间曼哈顿距离的最小值。

返回所有有效分割中可能的最大分割因子。

注意: 大小为 1 的组不贡献组内点对。当 n = 2(两个组都大小为 1)时,没有组内点对,所以定义分割因子为 0。

示例 1:

输入:points = [[0,0],[0,2],[2,0],[2,2]]
输出:4
解释:
我们将点分成两组:{[0, 0], [2, 2]} 和 {[0, 2], [2, 0]}。
- 第一组中,唯一的点对曼哈顿距离为 |0 - 2| + |0 - 2| = 4。
- 第二组中,唯一的点对曼哈顿距离为 |0 - 2| + |2 - 0| = 4。
这个分割的分割因子是 min(4, 4) = 4,这是最大的。

示例 2:

输入:points = [[0,0],[0,1],[10,0]]
输出:11
解释:
我们将点分成两组:{[0, 1], [10, 0]} 和 {[0, 0]}。
- 第一组中,唯一的点对曼哈顿距离为 |0 - 10| + |1 - 0| = 11。
- 第二组是单例,不贡献点对。
这个分割的分割因子是 11,这是最大的。

约束条件:

  • 2 <= points.length <= 500
  • points[i] = [xi, yi]
  • -10^8 <= xi, yi <= 10^8

解题思路

这道题的核心思想是使用二分搜索来找到最大的分割因子。

问题转化: 对于一个候选的分割因子 D,我们需要判断是否能够将所有点分成两组,使得每组内的最小曼哈顿距离都不小于 D。

关键洞察: 如果两个点的曼哈顿距离小于 D,那么它们必须被分到不同的组中。这样我们可以构建一个图:

  • 顶点:所有的点
  • 边:连接曼哈顿距离小于 D 的点对

二分图判断: 如果构建的图是二分图,说明可以将点分成两组满足条件;否则不可以。

算法步骤:

  1. 计算所有点对之间的曼哈顿距离
  2. 对所有距离进行排序,作为二分搜索的候选值
  3. 对每个候选值 D,构建约束图并检查是否为二分图
  4. 使用二分搜索找到最大的有效分割因子

时间复杂度: O(n² log n),其中 n 是点的数量。二分搜索需要 O(log n²) 次迭代,每次需要 O(n²) 时间构建图和检查二分图。

代码实现

class Solution {
public:
    int maxPartitionFactor(vector<vector<int>>& points) {
        int n = points.size();
        
        // 计算所有点对的曼哈顿距离
        vector<int> distances;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int dist = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);
                distances.push_back(dist);
            }
        }
        
        sort(distances.begin(), distances.end());
        distances.erase(unique(distances.begin(), distances.end()), distances.end());
        
        int left = 0, right = distances.size() - 1;
        int result = 0;
        
        while (left <= right) {
            int mid = (left + right) / 2;
            int D = distances[mid];
            
            if (isBipartite(points, D)) {
                result = D;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return result;
    }
    
private:
    bool isBipartite(vector<vector<int>>& points, int D) {
        int n = points.size();
        vector<vector<int>> graph(n);
        
        // 构建图:距离小于D的点之间有边
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int dist = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);
                if (dist < D) {
                    graph[i].push_back(j);
                    graph[j].push_back(i);
                }
            }
        }
        
        vector<int> color(n, -1);
        
        // 检查是否为二分图
        for (int i = 0; i < n; i++) {
            if (color[i] == -1) {
                if (!dfs(graph, i, 0, color)) {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    bool dfs(vector<vector<int>>& graph, int node, int c, vector<int>& color) {
        color[node] = c;
        for (int neighbor : graph[node]) {
            if (color[neighbor] == c || (color[neighbor] == -1 && !dfs(graph, neighbor, 1 - c, color))) {
                return false;
            }
        }
        return true;
    }
};
class Solution:
    def maxPartitionFactor(self, points: List[List[int]]) -> int:
        n = len(points)
        
        # 计算所有点对的曼哈顿距离
        distances = []
        for i in range(n):
            for j in range(i + 1, n):
                dist = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1])
                distances.append(dist)
        
        distances = sorted(set(distances))
        
        def is_bipartite(D):
            # 构建图:距离小于D的点之间有边
            graph = [[] for _ in range(n)]
            for i in range(n):
                for j in range(i + 1, n):
                    dist = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1])
                    if dist < D:
                        graph[i].append(j)
                        graph[j].append(i)
            
            color = [-1] * n
            
            def dfs(node, c):
                color[node] = c
                for neighbor in graph[node]:
                    if color[neighbor] == c or (color[neighbor] == -1 and not dfs(neighbor, 1 - c)):
                        return False
                return True
            
            # 检查是否为二分图
            for i in range(n):
                if color[i] == -1:
                    if not dfs(i, 0):
                        return False
            return True
        
        left, right = 0, len(distances) - 1
        result = 0
        
        while left <= right:
            mid = (left + right) // 2
            D = distances[mid]
            
            if is_bipartite(D):
                result = D
                left = mid + 1
            else:
                right = mid - 1
        
        return result
public class Solution {
    public int MaxPartitionFactor(int[][] points) {
        int n = points.Length;
        
        // 计算所有点对的曼哈顿距离
        var distances = new HashSet<int>();
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int dist = Math.Abs(points[i][0] - points[j][0]) + Math.Abs(points[i][1] - points[j][1]);
                distances.Add(dist);
            }
        }
        
        var sortedDistances = distances.OrderBy(x => x).ToArray();
        
        int left = 0, right = sortedDistances.Length - 1;
        int result = 0;
        
        while (left <= right) {
            int mid = (left + right) / 2;
            int D = sortedDistances[mid];
            
            if (IsBipartite(points, D)) {
                result = D;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return result;
    }
    
    private bool IsBipartite(int[][] points, int D) {
        int n = points.Length;
        var graph = new List<int>[n];
        for (int i = 0; i < n; i++) {
            graph[i] = new List<int>();
        }
        
        // 构建图:距离小于D的点之间有边
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int dist = Math.Abs(points[i][0] - points[j][0]) + Math.Abs(points[i][1] - points[j][1]);
                if (dist < D) {
                    graph[i].Add(j);
                    graph[j].Add(i);
                }
            }
        }
        
        var color = new int[n];
        Array.Fill(color, -1);
        
        // 检查是否为二分图
        for (int i = 0; i < n; i++) {
            if (color[i] == -1) {
                if (!Dfs(graph, i, 0, color)) {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    private bool Dfs(List<int>[] graph, int node, int c, int[] color) {
        color[node] = c;
        foreach (int neighbor in graph[node]) {
            if (color[neighbor] == c || (color[neighbor] == -1 && !Dfs(graph, neighbor, 1 - c, color))) {
                return false;
            }
        }
        return true;
    }
}
var maxPartitionFactor = function(points) {
    const n = points.length;
    if (n === 2) return 0;
    
    const manhattanDistance = (p1, p2) => Math.abs(p1[0] - p2[0]) + Math.abs(p1[1] - p2[1]);
    
    const getMinDistance = (group) => {
        if (group.length <= 1) return Infinity;
        let min = Infinity;
        for (let i = 0; i < group.length; i++) {
            for (let j = i + 1; j < group.length; j++) {
                min = Math.min(min, manhattanDistance(group[i], group[j]));
            }
        }
        return min;
    };
    
    let maxFactor = 0;
    
    for (let mask = 1; mask < (1 << n) - 1; mask++) {
        const group1 = [];
        const group2 = [];
        
        for (let i = 0; i < n; i++) {
            if (mask & (1 << i)) {
                group1.push(points[i]);
            } else {
                group2.push(points[i]);
            }
        }
        
        const min1 = getMinDistance(group1);
        const min2 = getMinDistance(group2);
        
        const partitionFactor = Math.min(min1, min2);
        maxFactor = Math.max(maxFactor, partitionFactor);
    }
    
    return maxFactor;
};

复杂度分析

复杂度类型复杂度说明
时间复杂度O(n² log n)其中n是点的数量。计算距离需要O(n²),二分搜索需要O(log n)次迭代,每次检查二分图需要O(n²)
空间复杂度O(n²)存储图的邻接表和颜色数组