2008-08-10 15 views
32

¿Cuál es el algoritmo más eficiente para encontrar el rectángulo con el área más grande que cabe en el espacio vacío?Puzzle: Encuentra el rectángulo más grande (problema de rectángulo máximo)

Digamos que la pantalla se ve (área '#' representa llena) así:

.................... 
..............###### 
##.................. 
.................### 
.................### 
#####............... 
#####............... 
#####............... 

Una posible solución es:

.................... 
..............###### 
##...++++++++++++... 
.....++++++++++++### 
.....++++++++++++### 
#####++++++++++++... 
#####++++++++++++... 
#####++++++++++++... 

Normalmente me gustaría disfrutar averiguar una solución. Aunque esta vez me gustaría evitar perder el tiempo buscando por mi cuenta, ya que esto tiene un uso práctico para un proyecto en el que estoy trabajando. ¿Hay una solución conocida?

Shog9 escribió:

Está su entrada una matriz (como se deduce por las otras respuestas), o una lista de oclusiones en la forma de, rectángulos colocados de tamaño arbitrario (como podría ser el caso en un sistema de ventanas cuando se trata de posiciones de ventanas)?

Sí, tengo una estructura que hace un seguimiento de un conjunto de ventanas colocadas en la pantalla. También tengo una grilla que hace un seguimiento de todas las áreas entre cada borde, ya sea que estén vacías o llenas, y la posición de píxel de su borde izquierdo o superior. Creo que hay alguna forma modificada que aprovecharía esta propiedad. ¿Sabes de alguno?

Respuesta

19

@lassevk

he encontrado el artículo de referencia, a partir DDJ: The Maximal Rectangle Problem

+4

Este es el tiempo O (mn), que es óptimo. –

+0

vea aquí también - http://www.geeksforgeeks.org/maximum-size-sub-matrix-with-all-1s-in-a-binary-matrix/ – roottraveller

3

Aquí hay una página que tiene algunos códigos y algunos análisis.

su problema particular comienza un poco más abajo en la página, buscar en la página para el texto problema rectángulo máxima.

http://www.seas.gwu.edu/~simhaweb/cs151/lectures/module6/module6.html

+3

Buenas explicaciones en esa página pero solo da un algoritmo O (mn^2), que no es óptimo (ver la respuesta de Mark Renouf, que es). -1. –

1

@lassevk

// 4. Outer double-for-loop to consider all possible positions 
    // for topleft corner. 
    for (int i=0; i < M; i++) { 
     for (int j=0; j < N; j++) { 

     // 2.1 With (i,j) as topleft, consider all possible bottom-right corners. 

     for (int a=i; a < M; a++) { 
      for (int b=j; b < N; b++) { 

HAHA ... O (n2 m2) .. Eso es probablemente lo que habría llegado con.

Veo que continúan desarrollando optimizaciones ... se ve bien, voy a leer.

18

Soy el autor del artículo que del Dr. Dobb y obtener de vez en cuando pregunté acerca de una implementación. Aquí hay uno simple en C:

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 

typedef struct { 
    int one; 
    int two; 
} Pair; 

Pair best_ll = { 0, 0 }; 
Pair best_ur = { -1, -1 }; 
int best_area = 0; 

int *c; /* Cache */ 
Pair *s; /* Stack */ 
int top = 0; /* Top of stack */ 

void push(int a, int b) { 
    s[top].one = a; 
    s[top].two = b; 
    ++top; 
} 

void pop(int *a, int *b) { 
    --top; 
    *a = s[top].one; 
    *b = s[top].two; 
} 


int M, N; /* Dimension of input; M is length of a row. */ 

void update_cache() { 
    int m; 
    char b; 
    for (m = 0; m!=M; ++m) { 
    scanf(" %c", &b); 
    fprintf(stderr, " %c", b); 
    if (b=='0') { 
     c[m] = 0; 
    } else { ++c[m]; } 
    } 
    fprintf(stderr, "\n"); 
} 


int main() { 
    int m, n; 
    scanf("%d %d", &M, &N); 
    fprintf(stderr, "Reading %dx%d array (1 row == %d elements)\n", M, N, M); 
    c = (int*)malloc((M+1)*sizeof(int)); 
    s = (Pair*)malloc((M+1)*sizeof(Pair)); 
    for (m = 0; m!=M+1; ++m) { c[m] = s[m].one = s[m].two = 0; } 
    /* Main algorithm: */ 
    for (n = 0; n!=N; ++n) { 
    int open_width = 0; 
    update_cache(); 
    for (m = 0; m!=M+1; ++m) { 
     if (c[m]>open_width) { /* Open new rectangle? */ 
     push(m, open_width); 
     open_width = c[m]; 
     } else /* "else" optional here */ 
     if (c[m]<open_width) { /* Close rectangle(s)? */ 
     int m0, w0, area; 
     do { 
      pop(&m0, &w0); 
      area = open_width*(m-m0); 
      if (area>best_area) { 
      best_area = area; 
      best_ll.one = m0; best_ll.two = n; 
      best_ur.one = m-1; best_ur.two = n-open_width+1; 
      } 
      open_width = w0; 
     } while (c[m]<open_width); 
     open_width = c[m]; 
     if (open_width!=0) { 
      push(m0, w0); 
     } 
     } 
    } 
    } 
    fprintf(stderr, "The maximal rectangle has area %d.\n", best_area); 
    fprintf(stderr, "Location: [col=%d, row=%d] to [col=%d, row=%d]\n", 
        best_ll.one+1, best_ll.two+1, best_ur.one+1, best_ur.two+1); 
    return 0; 
} 

Toma su entrada desde la consola. Podría, por ejemplo, tubería de este archivo a la misma:

16 12 
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 
0 0 0 1 1 0 0 1 0 0 0 1 1 0 1 0 
0 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 
0 0 0 0 1 1 * * * * * * 0 0 1 0 
0 0 0 0 0 0 * * * * * * 0 0 1 0 
0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 0 
0 0 1 0 0 0 0 1 0 0 1 1 1 0 1 0 
0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 

Y después de imprimir su entrada, que sería:

The maximal rectangle has area 12. 
Location: [col=7, row=6] to [col=12, row=5] 

La implementación anterior no es nada lujoso, por supuesto, pero es muy cerca de la explicación en el doctor El artículo de Dobb debe ser fácil de traducir a lo que sea necesario.

+4

Traducido a Java: https: //gist.github. com/mmadson/9637974 –

0

Implementé la solución de Dobbs en Java.

Sin garantía por nada.

package com.test; 

import java.util.Stack; 

public class Test { 

    public static void main(String[] args) { 
     boolean[][] test2 = new boolean[][] { new boolean[] { false, true, true, false }, 
       new boolean[] { false, true, true, false }, new boolean[] { false, true, true, false }, 
       new boolean[] { false, true, false, false } }; 
     solution(test2); 
    } 

    private static class Point { 
     public Point(int x, int y) { 
      this.x = x; 
      this.y = y; 
     } 

     public int x; 
     public int y; 
    } 

    public static int[] updateCache(int[] cache, boolean[] matrixRow, int MaxX) { 
     for (int m = 0; m < MaxX; m++) { 
      if (!matrixRow[m]) { 
       cache[m] = 0; 
      } else { 
       cache[m]++; 
      } 
     } 
     return cache; 
    } 

    public static void solution(boolean[][] matrix) { 
     Point best_ll = new Point(0, 0); 
     Point best_ur = new Point(-1, -1); 
     int best_area = 0; 

     final int MaxX = matrix[0].length; 
     final int MaxY = matrix.length; 

     Stack<Point> stack = new Stack<Point>(); 
     int[] cache = new int[MaxX + 1]; 

     for (int m = 0; m != MaxX + 1; m++) { 
      cache[m] = 0; 
     } 

     for (int n = 0; n != MaxY; n++) { 
      int openWidth = 0; 
      cache = updateCache(cache, matrix[n], MaxX); 
      for (int m = 0; m != MaxX + 1; m++) { 
       if (cache[m] > openWidth) { 
        stack.push(new Point(m, openWidth)); 
        openWidth = cache[m]; 
       } else if (cache[m] < openWidth) { 
        int area; 
        Point p; 
        do { 
         p = stack.pop(); 
         area = openWidth * (m - p.x); 
         if (area > best_area) { 
          best_area = area; 
          best_ll.x = p.x; 
          best_ll.y = n; 
          best_ur.x = m - 1; 
          best_ur.y = n - openWidth + 1; 
         } 
         openWidth = p.y; 
        } while (cache[m] < openWidth); 
        openWidth = cache[m]; 
        if (openWidth != 0) { 
         stack.push(p); 
        } 
       } 
      } 
     } 

     System.out.printf("The maximal rectangle has area %d.\n", best_area); 
     System.out.printf("Location: [col=%d, row=%d] to [col=%d, row=%d]\n", best_ll.x + 1, best_ll.y + 1, 
       best_ur.x + 1, best_ur.y + 1); 
    } 

} 
1

Después de luchar por lo mucho que he escrito este algoritmo ... Sólo hay que ver el código ...

Usted entiende that.This código habla !!

import java.util.Scanner; 
import java.util.Stack; 

/** 
* Created by BK on 05-08-2017. 
*/ 
public class LargestRectangleUnderHistogram { 
    public static void main(String... args) { 
     Scanner scanner = new Scanner(System.in); 
     int n = scanner.nextInt(); 
     int[] input = new int[n]; 
     for (int j = 0; j < n; j++) { 
      input[j] = scanner.nextInt(); 
     } 



     /* 
     * This is the procedure used for solving : 
     * 
     * Travel from first element to last element of the array 
     * 
     * If stack is empty add current element to stack 
     * 
     * If stack is not empty check for the top element of stack if 
     * it is smaller than the current element push into stack 
     * 
     * If it is larger than the current element pop the stack until we get an 
     * element smaller than the current element or until stack becomes empty 
     * 
     * After popping each element check if the stack is empty or not.. 
     * 
     * If stack is empty it means that this is the smallest element encountered till now 
     * 
     * So we can multiply i with this element to get a big rectangle which is contributed by 
     * 
     * this 
     * 
     * If stack is not empty then check for individual areas(Not just one bar individual area means largest rectangle by this) (i-top)*input[top] 
     * 
     * 
     * */ 

     /* 
     * Initializing the maxarea as we check each area with maxarea 
     */ 

     int maxarea = -1; 
     int i = 0; 
     Stack<Integer> stack = new Stack<>(); 
     for (i = 0; i < input.length; i++) { 

      /* 
      * Pushing the element if stack is empty 
      * */ 


      if (stack.isEmpty()) { 
       stack.push(i); 
      } else { 

       /* 
       * If stack top element is less than current element push 
       * */ 

       if (input[stack.peek()] < input[i]) { 
        stack.push(i); 
       } else { 

        /* 
        * Else pop until we encounter an element smaller than this in stack or stack becomes empty 
        * 
        * */ 


        while (!stack.isEmpty() && input[stack.peek()] > input[i]) { 

         int top = stack.pop(); 

         /* 
         * If stack is empty means this is the smallest element encountered so far 
         * 
         * So we can multiply this with i 
         * */ 

         if (stack.isEmpty()) { 
          maxarea = maxarea < (input[top] * i) ? (input[top] * i) : maxarea; 
         } 

         /* 
         * If stack is not empty we find areas of each individual rectangle 
         * Remember we are in while loop 
         */ 

         else { 
          maxarea = maxarea < (input[top] * (i - top)) ? (input[top] * (i - top)) : maxarea; 
         } 
        } 
        /* 
        * Finally pushing the current element to stack 
        * */ 

        stack.push(i); 
       } 
      } 
     } 

     /* 
     * This is for checking if stack is not empty after looping the last element of input 
     * 
     * This happens if input is like this 4 5 6 1 2 3 4 5 
     * 
     * Here 2 3 4 5 remains in stack as they are always increasing and we never got 
     * 
     * a chance to pop them from stack by above process 
     * 
     * */ 


     while (!stack.isEmpty()) { 

      int top = stack.pop(); 

      maxarea = maxarea < (i - top) * input[top] ? (i - top) * input[top] : maxarea; 
     } 

     System.out.println(maxarea); 
    } 
} 
Cuestiones relacionadas