这个问题在网上测试中被问到我。 在笛卡尔平面中给出了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点组合,并对所有组合应用上述方法,但这并不好。请指教。
答案 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)空间