web-dev-qa-db-fra.com

Programmation dynamique - le plus grand bloc carré

J'ai besoin de trouver le plus grand carré de 1 dans un fichier géant plein de 1 et de 0. Je sais que je dois utiliser la programmation dynamique. Je le stocke dans un tableau 2D. Toute aide avec l'algorithme pour trouver le plus grand carré serait formidable, merci!

exemple d'entrée:

1 0 1 0 1 0
1 0 1 1 1 1
0 1 1 1 1 1
0 0 1 1 1 1
1 1 1 1 1 1

répondre:

1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

Mon code jusqu'à présent:

int Square (Sq[int x][int y]) {
   if (Sq[x][y]) == 0) {
       return 0;
   }
   else {
       return 1+MIN( Sq(X-1,Y), Sq(X,Y-1), Sq(X-1,Y-1) );
   }
}

(en supposant des valeurs déjà entrées dans le tableau)

int main() {
    int Sq[5][6]; //5,6 = bottom right conner
    int X = Square(Sq[5][6]);
}

Comment puis-je continuer à partir de là?

40
batt

Voici un croquis de la solution:

Pour chacune des cellules, nous garderons un compteur de la taille d'un carré en utilisant cette cellule en haut à gauche. Clairement, toutes les cellules avec 0 auront 0 comme nombre.

Commencez à itérer à partir de la cellule en bas à droite et allez en bas à gauche, puis allez à une rangée et répétez.

À chaque analyse, procédez comme suit:

  1. Si la cellule a 0, attribuez count=0
  2. Si la cellule a 1 et est une cellule Edge (bord inférieur ou droit uniquement), affectez count=1
  3. Pour toutes les autres cellules, vérifiez le nombre de cellules à droite, à droite en dessous et en dessous. Prenez le minimum d'entre eux et ajoutez 1 et attribuez cela au compte. Gardez une variable globale max_count Pour garder une trace du nombre maximum jusqu'à présent.

À la fin de la traversée de la matrice, max_count Aura la valeur souhaitée.

La complexité n'est plus que le coût de la traversée de la matrice.

Voici à quoi ressemblera la matrice après la traversée. Les valeurs entre parenthèses sont les nombres, c'est-à-dire le plus grand carré qui peut être créé en utilisant la cellule en haut à gauche.

1(1) 0(0) 1(1) 0(0) 1(1) 0(0)
1(1) 0(0) 1(4) 1(3) 1(2) 1(1)
0(0) 1(1) 1(3) 1(3) 1(2) 1(1)
0(0) 0(0) 1(2) 1(2) 1(2) 1(1)
1(1) 1(1) 1(1) 1(1) 1(1) 1(1)

Implémentation en Python

def max_size(mat, ZERO=0):
    """Find the largest square of ZERO's in the matrix `mat`."""
    nrows, ncols = len(mat), (len(mat[0]) if mat else 0)
    if not (nrows and ncols): return 0 # empty matrix or rows
    counts = [[0]*ncols for _ in xrange(nrows)]
    for i in reversed(xrange(nrows)):     # for each row
        assert len(mat[i]) == ncols # matrix must be rectangular
        for j in reversed(xrange(ncols)): # for each element in the row
            if mat[i][j] != ZERO:
                counts[i][j] = (1 + min(
                    counts[i][j+1],  # east
                    counts[i+1][j],  # south
                    counts[i+1][j+1] # south-east
                    )) if i < (nrows - 1) and j < (ncols - 1) else 1 # edges
    return max(c for rows in counts for c in rows)
79
Joy Dutta

LSBRA(X,Y) signifie "le plus grand carré avec en bas à droite en X, Y"

Pseudocode:

LSBRA(X,Y):
   if (x,y) == 0:
       0
   else:
       1+MIN( LSBRA(X-1,Y), LSBRA(X,Y-1), LSBRA(X-1,Y-1) )

(Pour les cellules Edge, vous pouvez ignorer la partie MIN et renvoyer simplement 1 si (x, y) n'est pas 0.)

Travaillez en diagonale à travers la grille en "vagues", comme suit:

    0 1 2 3 4
  +----------
0 | 1 2 3 4 5
1 | 2 3 4 5 6
2 | 3 4 5 6 7
3 | 4 5 6 7 8

ou alternativement, travaillez de gauche à droite, de haut en bas, tant que vous remplissez des cellules Edge.

    0 1 2 3 4
  +----------
0 | 1 2 3 4 5
1 | 6 7 8 9 .
2 | . . . . .
3 | . . . . .

De cette façon, vous ne rencontrerez jamais un calcul où vous n'avez pas encore calculé les données nécessaires - donc tous les LSBRA() "appels" ne sont en fait que des tables de recherche de vos résultats de calcul précédents (d'où la programmation dynamique aspect).

Pourquoi ça marche

Afin d'avoir un carré avec un coin inférieur droit en X, Y - il doit contenir les carrés qui se chevauchent d'une dimension en moins qui touchent chacun des 3 autres coins. En d'autres termes, avoir

XXXX
XXXX
XXXX
XXXX

vous devez aussi avoir ...

XXX.    .XXX    ....    ....
XXX.    .XXX    XXX.    ....
XXX.    .XXX    XXX.    ....
....    ....    XXX.    ...X

Tant que vous avez ces 3 carrés (chacun des contrôles LSBRA) de taille N plus le carré actuel est également "occupé", vous aurez un carré de taille (N + 1).

8
Amber

Le premier algorithme qui me vient à l'esprit est:

  1. '&&' colonne/ligne 1 avec colonne/ligne 2 si, c'est-à-dire faire une opération '&&' entre chaque entrée et son entrée correspondante dans l'autre colonne/ligne.
  2. Vérifiez la colonne résultante, s'il y a une longueur de 2 1, cela signifie que nous frappons un carré 2x2.
  3. Et la colonne suivante avec le résultat des deux premiers. S'il y a des longueurs de 3 1, nous avons atteint un carré de 3x3.
  4. Répétez jusqu'à ce que toutes les colonnes aient été utilisées.
  5. Répétez 1-4 en commençant à la colonne 2.

Je ne vais pas vous montrer l'implémentation car elle est assez simple et votre problème ressemble à des devoirs. En outre, il existe probablement des moyens beaucoup plus efficaces de le faire, car cela deviendra lent si l'entrée était très grande.

3
DeusAduro

Soit la matrice d'entrée est M: n x m

T[i][j] est la matrice DP qui contient le plus grand côté carré avec des carrés en bas à droite (i,j).

Règle générale pour remplir le tableau:

if (M[i][j] == 1) {
  int v = min(T[i][j-1], T[i-1][j]);
  v = min(v, T[i-1][j-1]);
  T[i][j] = v + 1;
}
else 
  T[i][j] = 0;

La taille du carré résultant est la valeur maximale dans T.

Remplissage T[i][0] et T[0][j] est trivial.

Je ne sais pas si cet algo peut être utilisé pour votre fichier énorme, mais vous n'avez pas besoin de stocker la matrice entière T mais uniquement la version actuelle et précédente lignes uniquement.

Les notes suivantes peuvent aider à comprendre l'idée générale:

  • tous les carrés avec des angles inférieurs droits (i-1, j), (i, j-1), (i-1, j-1) de taille s sont à l'intérieur du carré de l'angle inférieur droit (i, j) de taille s +1.
  • s'il y a un carré de taille s + 1 avec le coin inférieur droit en (i, j), alors la taille du carré maximal avec des angles inférieurs droits (i-1, j), (i, j-1), (i-1, j-1) est au moins s.
  • Le contraire est également vrai. Si la taille d'au moins un carré avec des angles inférieurs droits à (i-1, j), (i, j-1), (i-1, j-1) est inférieure à s, alors la taille du carré avec le coin inférieur droit en (i, j) ne peut pas être plus grand que s + 1.
2
sergtk

La clé ici est que vous pouvez garder une trace de la racine de la zone au lieu de la zone réelle, en utilisant la programmation dynamique.

L'algorithme est le suivant:

Stockez un tableau 2D d'entiers appelé max-carré, où un élément à l'indice i, j représente la taille du carré dans lequel il se trouve avec i, j étant le coin inférieur droit. (si max [i, j] = 2, cela signifie que l'indice i, j est le coin inférieur droit d'un carré de taille 2 ^ 2 = 4)

Pour chaque indice i, j:

si en i, j l'élément est 0, alors définissez max-square i, j sur 0.

sinon:

Trouver le minimum de max-square [i - 1, j] et max-square [i, j - 1] et max-square [i - 1] [j -1]. définissez max-square [i, j] sur 1 + le minimum de 3. Inductivement, vous finirez par remplir le tableau max-square. Trouvez/ou gardez une trace de la valeur maximale dans le processus, renvoyez cette valeur ^ 2.

Jetez un oeil à ces solutions que les gens ont proposées: https://leetcode.com/discuss/questions/oj/maximal-square?sort=votes

1
Han Sheng Huang

OK, la manière la plus inefficace mais simple serait:

  1. sélectionnez le premier élément. cochez si 1, si c'est le cas, vous avez un carré 1x1.

  2. cochez une en dessous et une à droite, si 1, puis cochez la ligne 2 col 2, si 1, carré 2x2.

  3. cochez la ligne 3 col 1, col 2 et col 3, plus la ligne 1 col 3, la ligne 2 col 3, si 1, 3x3.

  4. Donc, fondamentalement, vous continuez à développer la ligne et le col ensemble et vérifiez toutes les cellules à l'intérieur de leurs limites. Dès que vous atteignez un 0, il est cassé, vous vous déplacez donc sur 1 point d'affilée et recommencez.

  5. À la fin du rang, passez au rang suivant.

  6. jusqu'à la fin.

Vous pouvez probablement voir comment ceux-ci s'intègrent dans les boucles while, etc., et comment &&s peut être utilisé pour vérifier les 0, et en le regardant, vous remarquerez peut-être aussi comment il peut être accéléré. Mais comme l'autre réponse vient de le mentionner, cela ressemble un peu à des devoirs, nous laisserons donc le code réel à vous.

Bonne chance!

1
Mark Mayo

Soit N la quantité de cellules du tableau 2D. Il existe un algorithme très efficace pour lister tous les rectangles vides maximum. Le plus grand carré vide se trouve à l'intérieur d'un de ces rectangles vides, et le trouver est trivial une fois que la liste des rectangles vides maximum a été calculée. Un article présentant un O(N) algorithme pour créer une telle liste peut être trouvé sur www.ulg.ac.be/telecom/rectangles ainsi que le code source (non optimisé). Notez qu'il existe une preuve (voir l'article) que le nombre de plus grands rectangles vides est délimité par N. Par conséquent, la sélection du plus grand carré vide peut être effectuée dans O (N), et la méthode globale est également O (N). En pratique, cette méthode est très rapide. L'implémentation est très facile à faire, car le code entier ne doit pas dépasser 40 lignes de C (l'algorithme pour lister tous les rectangles vides maximum prend environ 30 lignes de C ).

0
S. Piérard