关于使用两个矩形封闭点的算法难题

时间:2016-04-02 17:50:25

标签: java algorithm dynamic-programming graph-theory puzzle

我已经在这个问题上工作了两天,我能做的最好的事情就是蛮力解决方案,效率不够。

您将获得一系列正坐标点,范围从(0, 0)(1 billion, 1 billion)。您必须将所有点仅包含两个具有最小总面积的矩形。矩形必须具有平行于x轴和y轴的边。矩形不能重叠,共享相同的边界计数与重叠。您**can**的{​​{1}}个0矩形区域为零。两个矩形的面积之和为0

您还必须找到包含所有点的最小可能区域的单个矩形。此区域为**X**

您正试图找到**Y**

对于以下示例,答案为**Y** - **X**

**Y** - **X** = 107

非常感谢提供代码,如果您这样做,请尽可能使用Java或C ++。

2 个答案:

答案 0 :(得分:4)

我不想破坏游戏。

从大矩形开始。然后你可以分割点的每个x或y。

按x对点进行一次排序,一次按y。

垂直拆分:

#######
#######

   #######
   #######

水平分割:

##
##    ####
      ####
      ####

在坐标处分割产生两组点,其中两个矩形半部都很容易缩小。

因评论而添加了解决方案

作为Point类,我实际使用int[2],因此x / y选项可以作为for-index。另一方面,我必须创建一个类AreaCollector,其中一个简单的Rectangle就足够了。

我收集的矩形点也是;如果没有它们,代码就会变得更小。

static private class AreaCollector {

    private final int[] lwb = new int[] { Integer.MAX_VALUE, Integer.MAX_VALUE };
    private final int[] upb = new int[] { Integer.MIN_VALUE, Integer.MIN_VALUE };

    public void add(int[] point) {
        if (point[0] < lwb[0]) {
            lwb[0] = point[0];
        }
        if (point[1] < lwb[1]) {
            lwb[1] = point[1];
        }
        if (point[0] > upb[0]) {
            upb[0] = point[0];
        }
        if (point[1] > upb[1]) {
            upb[1] = point[1];
        }
    }

    public int getArea() {
        if (upb[0] == Integer.MIN_VALUE) { /// Zero points added.
            return 0;
        }
        return (upb[0] - lwb[0]) * (upb[1] - lwb[1]);
    }
}

public int solve(int[][] points) {
    AreaCollector ac = new AreaCollector();
    for (int[] point : points) {
        ac.add(point);
    }
    final int y = ac.getArea();
    final int n = points.length;

    // Best solution sofar:
    int[][] ascPoints = Arrays.copyOf(points, n);
    int[][] descPoints = new int[0][];
    int bestX = y + 0;

    for (int direction = 0; direction < 2; ++direction) {
        final int dir = direction;
        Arrays.sort(points, Comparator.comparingInt((pt) -> pt[dir]));

        int[] ascAreas = new int[n];
        AreaCollector ascAC = new AreaCollector();
        for (int i = 0; i < n; ) {
            int[] point = points[i];
            int coord = point[direction];
            for (int j = i; j < n && points[j][direction] == coord; ++j) {
                ascAC.add(points[j]);
            }
            int area = ascAC.getArea();
            for (int j = i; j < n && points[j][direction] == coord; ++j) {
                ascAreas[j] = area;
                ++i;
            }
        }

        int[] descAreas = new int[n];
        AreaCollector descAC = new AreaCollector();
        for (int i = n - 1; i >= 0; ) {
            int[] point = points[i];
            int coord = point[direction];
            for (int j = i; j >= 0 && points[j][direction] == coord; --j) {
                descAC.add(points[j]);
            }
            int area = descAC.getArea();
            for (int j = i; j >= 0 && points[j][direction] == coord; --j) {
                descAreas[j] = area;
                --i;
            }
        }

        int bestI = -1;
        for (int i = 0; i < n- 1; ++i) {
            if (points[i][direction] != points[i + 1][direction]) {
                int x = ascAreas[i] + descAreas[i + 1];
                if (x < bestX) {
                    bestX = x;
                    bestI = i;
                }
            }
        }
        if (bestI != -1) {
            ascPoints = Arrays.copyOfRange(points, 0, bestI + 1);
            descPoints = Arrays.copyOfRange(points, bestI + 1, n);
        }
    }
    return y -bestX;
}

作为比较器,我使用了java 8简洁表示法。如您所见,手动编码部分的复杂性为O(N),取代Arrays.sort O(N.log N)。

答案 1 :(得分:3)

这是Java的解决方案。在计算区域Y之后,它首先按X坐标对坐标进行排序,然后通过在每个X坐标处将数组分成两半来计算矩形的面积(如果两个坐标具有相同的X值,则进行特殊处理)。然后它为Y坐标做同样的事情。最小矩形区域是生成的X区域。

import java.util.Arrays;
import java.util.Comparator;

public class Puzzle {

    public static void main(String[] args) {

        int[][] COORDINATES_1 = { { 4, 2 }, { 8, 10 }, { 1, 1 }, { 9, 12 }, { 14, 7 }, { 2, 3 } };
        int[][] COORDINATES_2 = { { 2, 1 }, { 2, 2 }, { 3, 1 }, { 3, 3 }, { 4, 3 }, { 5, 3 }, { 5, 4 }, { 6, 4 } };
        int[][] COORDINATES_3 = { { 4, 2 } };

        solve(COORDINATES_1);
        solve(COORDINATES_2);
        solve(COORDINATES_3);
    }

    public static void solve(int[][] coordinates) {

        int size = coordinates.length;
        int y = calcMinRectArea(coordinates, 0, size);

        // sort by x coordinates
        Arrays.sort(coordinates, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });

        int x = y;
        for (int i = 1; i < size; i++) {
            if (coordinates[i][0] == coordinates[i - 1][0])
                continue; // several coordinates with the same x coordinates
            x = Math.min(calcMinRectArea(coordinates, 0, i) + calcMinRectArea(coordinates, i, size - i), x);
        }

        // sort by y coordinates
        Arrays.sort(coordinates, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        });

        for (int i = 1; i < size; i++) {
            if (coordinates[i][1] == coordinates[i - 1][1])
                continue; // several coordinates with the same y coordinates
            x = Math.min(calcMinRectArea(coordinates, 0, i) + calcMinRectArea(coordinates, i, size - i), x);
        }

        System.out.printf("Y = %d, X = %d, Y - X = %d\n", y, x, y - x);
    }

    private static int calcMinRectArea(int[][] coords, int start, int length) {

        if (length == 0)
            return 0;

        int minX = coords[start][0];
        int maxX = minX;
        int minY = coords[start][1];
        int maxY = minY;

        for (int i = start + 1; i < start + length; i++) {
            int x = coords[i][0];
            minX = Math.min(minX, x);
            maxX = Math.max(maxX, x);
            int y = coords[i][1];
            minY = Math.min(minY, y);
            maxY = Math.max(maxY, y);
        }

        return (maxX - minX) * (maxY - minY);
    }
}