在给定的N个点之间包含K点的最小区域的正方形

时间:2015-06-20 06:33:29

标签: java algorithm

这个问题在网上测试中被问到我。 在笛卡尔平面中给出了N个点。将给出整数K. 目的是找到包围至少K点的正方形(最小)区域。 正方形的边应平行于轴。正方形的顶点应为整数。任何位于侧面的点都不被认为是在广场内。

我只能在K = N时解决它(即所有点都在正方形内)。

我的解决方案是 -

    static int minarea(int[] x, int[] y, int k) {
    //Find max y
    int maxVal = Integer.MIN_VALUE;
    for(int i : y){
        if(i > maxVal){
            maxVal = i;
        }
    }
    //Find min y
    int minVal = Integer.MAX_VALUE;
    for(int i : x){
        if(i < minVal){
            minVal = i;
        }
    }

    int yLength = (maxVal-minVal)+2;

    //Find max x
    maxVal = Integer.MIN_VALUE;
    for(int i : x){
        if(i > maxVal){
            maxVal = i;
        }
    }
    //Find min x
    minVal = Integer.MAX_VALUE;
    for(int i : x){
        if(i < minVal){
            minVal = i;
        }
    }

    int xLength = (maxVal-minVal)+2;
    int sqSide = (yLength > xLength)?yLength:xLength;
    return sqSide*sqSide;

}

一般解决方案的一种方法是在N个点之间找到所有可能的K点组合,并对所有组合应用上述方法,但这并不好。请指教。

3 个答案:

答案 0 :(得分:2)

可以看出,我们总是可以移动方块,使其在左边和底边有点。我们将遍历正方形的左边和底边的所有组合。 然后我们需要找到正方形的上边缘或右边缘。对于每一点,我们都可以确定它将处于什么边缘。例如,如果point.x - left > point.y - bottom,则点将位于正方形的右边缘,结果区域将为( point.x - left )^2。我们需要按正方形区域对点进行排序:area = ( max( point.x - left, point.y - bottom ) )^2并从此排序列表中选择K点。它将是包含指定左下角的至少K点的最小正方形。这个解决方案的复杂性是O(n^3),这不是很好,但它比迭代K点的所有组合更快。我在java中的解决方案:https://ideone.com/139C7A

答案 1 :(得分:0)

static void initBounds(int[] x, int[] y)
    {
        minX = x[0];
        maxX = x[0];
        minY = y[0];
        maxY = y[0];
        for(int i = 1; i < x.length; i++){
            if(x[i] > maxX)
                maxX = x[i];
            if(x[i] < minX)
                minX = x[i];
            if(y[i] > maxY)
                maxY = y[i];
            if(y[i] < minY)
                minY = y[i];
        }
    }

    static int countEnclosingPoints(int[] x, int[] y, int sx1, int sy1, int sx2, int sy2)
    {
        int count = 0;
        for(int i = 0; i < x.length; i++)
        {
            if(x[i] > sx1 && x[i] < sx2 && y[i] > sy1 && y[i] < sy2)
                count++;
        }
        return count;
    }

    static int minX;
    static int minY;
    static int maxX;
    static int maxY;

    static long minarea(int[] x, int[] y, int k) {
        long area = 0;
        initBounds(x, y);
        int xDiff = maxX - minX;
        int yDiff = maxY - minY;

        int sx1 = minX - 1;
        int sy1 = minY - 1;

        int sideDiff = Math.abs(xDiff - yDiff) + 1;

        int sx2; 
        int sy2;

        if(xDiff > yDiff){
            sx2 = maxX + 1;
            sy2 = maxY + sideDiff;
        } else {
            sx2 = maxX + sideDiff;
            sy2 = maxY + 1;
        }
        area = (sx2 - sx1) * (sx2 - sx1);

        int p, q, r, s;
        int minSize = (int) Math.sqrt(k) + 1;
        for(p = sx1; p < sx2 - minSize; p++) {
            for(q = sy1; q < sy2 - minSize; q++) {
                int xd = sx2 - p; int yd = sy2 - q;
                int sd = (xd < yd)? xd : yd;
                for(int i = sd; i >= minSize; i--){
                    int count = countEnclosingPoints(x, y, p, q, p + i, q + i);
                    if(count >= k) {
                        long currArea = i * i;
                        if(currArea < area)
                            area = currArea;
                    }
                    else
                        break;
                }
            }
        }
        return area;
    }

生成所有可能的区域(sqrt(k))*(sqrt(k))到所有点的最大边界。方块的左下角可以是边界方块内的任何位置。考虑诸如容纳至少k个点(sqrt(k))所需的最小尺寸平方的约束。如果一个正方形包含至少k个点且面积小于当前最小面积,则更新区域。

答案 2 :(得分:0)

看起来Michiel Smid有一篇关于这个问题的论文:

http://pubman.mpdl.mpg.de/pubman/item/escidoc:1834660:2/component/escidoc:1857755/MPI-I-93-116.pdf

  

给出了一种算法,给定了平面中的一组n个点   整数k,2≤k≤n,找到具有最小包围轴 - 平行的k个点   广场。该算法的运行时间为O(n log n + kn log2   k)并使用O(n)   空间。先前最着名的针对该问题的算法需要O(k   2n log n)   时间并使用O(kn)空间