2015-04-13 1 views
2

У меня есть код, который пересекает сетку свободных и «заблокированных» позиций; и находит максимально возможный прямоугольник.Найти большой прямоугольник в сетке, который лежит на границе и содержит точку

Это работает просто отлично. Тем не менее, мне нужно найти прямоугольник с более конкретными критериями. Учитывайте следующее:

00X0000000 
000000#### 
########## 
####000000 
0000000000 
0000000000 

На этой схеме; 0 указывает доступные позиции, # обозначает занимаемые позиции, а X обозначает желаемую точку.

Код, который у меня есть, теперь найдет нижнюю правую область сетки, так как это действительно самый большой прямоугольник. Однако ответ, который я хочу, содержит позицию X (верхний левый прямоугольник).

Я не знаю, как добавить дополнительные критерии. Я попытался также отслеживать координаты; выкидывая результаты, которые не содержат X; но это не работало все время. При определенных условиях я бы получил прямоугольники, которые были меньше желаемого (вероятно, из-за того, что я пересекал сетку)

Все алгоритмы, которые я нашел, по-видимому, способны находить самый большой прямоугольник, а координаты прямоугольник. Я не смог найти ни одного, который мог бы исключить результаты, которые не содержат определенную точку надежно.

Может ли кто-нибудь указать мне в правильном направлении и/или обеспечить реализацию?

EDIT: : Поскольку комментарии не могут иметь символы новой строки, мне нужно использовать эту область, чтобы объяснить проблемы с реализацией shole. Он терпит неудачу в таких случаях:

#0#X 
###0 
#000 
#### 
0#00 
0000 
0000 

В приведенном выше примере, координаты возвращаются образуют прямоугольник как это:

0#X 
##0 
000 

ответ должен быть 1x3, а не 3х3

Другой пример отказа:

0#X# 
0000 
00## 
000# 
0000 

возвращается:

#X 
00 

В этом случае ответ должен быть 1x2, вместо этого он считает, что это 2x2.

В некоторых случаях он возвращает области, которые больше, чем площадь всей сетки, с координатами за пределами допустимых координат.

Это работает ИНОГДА, но чаще это неверно, чем правильно. Он всегда возвращает область, содержащую точку; просто неправильная область/координаты.

+0

Не являются ли нижние две линии наибольшим прямоугольником? «Площадь» - 20 (количество нулей). В правом нижнем углу находится область 18. – user1952500

+0

Вы правы. Я это заметил. Основная идея состоит в том, что я не хочу возвращать ни один из нижних прямоугольников, только тот, у которого есть X-точка. – dmarra

+0

Вы можете начать с X и идти вверх, вниз, влево и вправо (как пульсация) и развернуть, пока не получите «#». Координаты вблизи хеша будут одной из конечных точек такого максимального прямоугольника. – user1952500

ответ

1

Интересная проблема, вот мой O (R * C) алгоритм с R = # строками, C = #columns

Идея проста, перебор каждая точка в нижних границах потенциальных прямоугольников, то попробуйте подняться как можно выше, чтобы получить максимальную высоту, а затем получить самое большое левое и правое расстояние этой вертикальной линии

enter image description here

Это проверяет все возможные прямоугольники, но мы должны сделать это быстро. Мы делаем это путем динамического программирования по строкам, для каждого (i, j), вычисляя максимальную высоту, максимально возможное расстояние слева и справа.

Тогда мы можем проверить, если X находится внутри этого потенциального прямоугольника, если да, то я добавить бесконечное большое смещение площади этого прямоугольника, достаточно велико, что любой прямоугольник не содержит X, будет меньше, чем это , в то время как другие прямоугольники, если они содержат X и больше, мы можем выбрать этот прямоугольник.

После окончания алгоритма мы получаем максимум Площадь + смещение, мы вычитаем смещение от него и получим область обратно.

Это более легко понять, если вы запустите код (C++) и посмотреть журнал ниже, со следующими входными:

6 10 
00X0000000 
000000#### 
########## 
####000000 
0000000000 
0000000000 

#include<bits/stdc++.h> 
 
using namespace std; 
 

 
char w[20][20]; 
 
int tl[20][20] = {0}, tr[20][20] = {0}, h[20][20] = {0}, l[20][20]={0}, r[20][20]={0}; 
 
int ans, R,C, xr,xc, ar1, ac1, ar2, ac2; 
 

 
int largestRect() 
 
{ 
 
    int area = 0; 
 
    
 
    for(int i=0; i<20;i++) for(int j=0; j<20;j++) l[i][j] = r[i][j] = 1<<28; 
 
    
 
    \t for(int i=1; i<=R;i++){ 
 
    \t  for(int j=1; j<=C;j++) 
 
    \t \t  if(w[i][j]!='#') tl[i][j] = tl[i][j-1]+1; 
 
    \t \t \t else tl[i][j] = 0; 
 
    
 
    \t \t for(int j=C; j>=1; j--) 
 
    \t \t \t if(w[i][j]!='#') tr[i][j] = tr[i][j+1]+1; 
 
    \t \t \t else tr[i][j] = 0; 
 
    \t \t 
 
    \t \t for(int j=1; j<=C; j++) 
 
    \t \t \t if(w[i][j]!='#') h[i][j] = h[i-1][j]+1; 
 
    \t \t \t else h[i][j] = 0; 
 
    \t \t 
 
    \t \t for(int j=1; j<=C; j++) 
 
    \t \t \t if(w[i][j] != '#') l[i][j] = min(tl[i][j], l[i-1][j]); 
 
    \t \t 
 
    \t \t for(int j=1; j<=C; j++) 
 
    \t \t \t if(w[i][j] != '#') r[i][j] = min(tr[i][j], r[i-1][j]); 
 
    \t 
 
    \t \t 
 
    \t \t for(int j=1; j<=C;j++){ 
 
    \t \t \t int offset = 0; 
 
    \t \t \t if((r[i][j]+l[i][j]-1)*h[i][j] > 0){ 
 
    \t \t \t  if(xr >= i-h[i][j]+1 && xr <= i && xc >= j-l[i][j]+1 && xc <= j+r[i][j]-1) offset = 1<<28; 
 
    \t \t \t \t printf("Top left = (%d,%d) Bottom right = (%d,%d)\n", i-h[i][j]+1, j-l[i][j]+1,i, j+r[i][j]-1); 
 
    \t \t \t \t printf("Area = %d\n", (r[i][j]+l[i][j]-1)*h[i][j]); 
 
    \t \t \t \t printf("Included X? %d\n", xr >= i-h[i][j]+1 && xr <= i && xc >= j-l[i][j]+1 && xc <= j+r[i][j]-1); 
 
    \t \t \t \t printf("Area with offset = %d\n\n", (r[i][j]+l[i][j]-1)*h[i][j] + offset); 
 
    \t \t \t \t \t 
 
    \t \t \t \t if(area <= (r[i][j]+l[i][j]-1)*h[i][j] + offset){ 
 
    \t \t \t \t \t area = max(area, (r[i][j]+l[i][j]-1)*h[i][j] + offset); 
 
    \t \t \t \t \t \t ar1 = i-h[i][j]+1; ac1 = j-l[i][j]+1; ar2 = i; ac2 = j+r[i][j]-1; 
 
    \t \t \t \t } 
 
    \t \t \t } 
 
    \t \t } 
 
    \t } 
 
    
 
    return area; 
 
} 
 

 
int main(){ 
 
\t scanf("%d %d", &R, &C); 
 
\t 
 
\t for(int i=0; i<20;i++) for(int j=0; j<20;j++) w[i][j] = '#'; 
 
\t 
 
\t for(int i=1; i<=R; i++){ 
 
\t \t getchar(); 
 
\t \t for(int j=1; j<=C; j++){ 
 
\t \t \t w[i][j] = getchar(); \t 
 
\t \t \t if(w[i][j] == 'X'){ xr = i; xc = j;} 
 
\t \t } 
 
\t } 
 
\t 
 
\t ans = largestRect() - (1<<28); 
 

 
\t printf("Largest Rect Containing X is (%d,%d) to (%d,%d), with area = %d\n", ar1, ac1, ar2, ac2, ans); 
 
\t return 0; \t 
 
}

EDITED ЧАСТЬ: Алгоритм/код Разъясняется в деталях

Прежде всего, вот контур нашего O (R * C) algo rithm:

  1. перебор каждый пустой точка в сетке, использовать его в качестве точки нижней границы нашего потенциального крупнейшего пустого прямоугольника. Это O (R * C)
  2. Для каждой точки, я хочу идти вверх, насколько это возможно, давайте определим h [i] [j] - это массив, означающий максимальную высоту, с которой вы можете добраться, начиная с (i, к)
  3. Теперь я хочу знать, как далеко я могу пойти налево и направо, в пределах этого вертикального диапазона сегмента
  4. я получаю высоту, я получаю самую длинную длину справа и слева я могу достичь в пределах этой высоты, поэтому я могу, конечно, рассчитать площадь этого прямоугольника
  5. Получите максимум от площади каждого прямоугольника, повторяя шаг 1-4

Затем мы увидим, как сделать шаг 2-3 в алгоритме, мы будем использовать Динамическое программирование.

Давайте определим несколько массива:

h[i][j] := the highest height you can reach starting at (i,j) 
tr[i][j] := the longest distance to right you can reach starting at (i,j) 
tl[i][j] := the longest distance to left you can reach starting at (i,j) 

Один должен быть в состоянии видеть, что эти три массива может быть предвычислением с использованием O (R * C)

h[i][j] = h[i-1][j]+1 if(i,j) is empty, else h[i][j] = 0 
similarly 
tr[i][j] = tr[i][j+1]+1 if(i,j) is empty, else tr[i][j] = 0 
tl[i][j] = tl[i][j-1]+1 if(i,j) is empty, else tl[i][j] = 0 

Теперь определит еще два массива

l[i][j] := the longest distance to right you can reach starting from 
line (i,j) and (i-h[i][j]+1,j) (That's simply just put h[i][j] into consideration) 

Как и в случае с r [i] [j], убедитесь, что вы выдержать разницу между tl [i] [j] и l [i] [j] (tr [i] [j] и r [i] [j])

Теперь l [i] [j] и r [i] [j] также может быть предварительно вычислено с использованием O (R * C)!

Небольшое отличие состоит в том, что они нуждаются в час [I] [J] и тр [I] [J] (или TL [I] [J]), чтобы вычислить его,

так ч [I] [ j], tl [i] [j], tr [i] [j] должно быть вычислено до l [i] [j] и r [i] [j].

l[i][j] = min(l[i-1][j], tl[i][j]) if(i,j) is empty, else l[i][j] = tl[i][j] 
r[i][j] = min(r[i-1][j], tr[i][j]) if(i,j) is empty, else r[i][j] = tr[i][j] 

Вы можете думать, что он говорит следующие утверждения:

л [я] [J] является минимум ТЛ [X] [J], X в [Шс [я ] [J] + 1, я]

Так л [я] [J] максимальное расстояние влево в пределах (I, J) на максимальную высоту

Подобно г [I] [ j]

Так что мой код использует два для петель делают именно динамическое программирование описано выше, это кажется сложным, потому что я не предвычисление их всех перед их использованием

Я вычислить эти массивы и использовать их для вычисления прямоугольника площадь на лету. Говоря, вы всегда можете предварительно скомпоновать все эти массивы, а затем вычислить максимальные площади впоследствии, сложность по времени еще равна O (R * C)

+0

Я попытался преобразовать это в C# (язык, необходимый для моего проекта), но я понял, что не понимаю этот код. Почему все эти массивы 20x20, когда вход 6x10? Почему массивы l и r инициализируются такими высокими значениями, но не tr и tl? Это просто C++, а дополнительные элементы даже не используются? – dmarra

+0

все-таки работает? и какую часть вы не понимаете? Я пытаюсь объяснить больше, на первый взгляд трудно понять, потому что за ним есть какая-то умная концепция DP. – shole

+0

Я еще не знаю, потому что я не закончил преобразование. Ха-ха. Пройдя эту строку за строкой, я думаю, что при назначении r [i] [j] может быть ошибка. Разве этот цикл не должен идти в противоположном направлении? – dmarra