找到n * n网格中的最大正方形

时间:2016-03-08 02:56:40

标签: python algorithm performance optimization

我有一个问题,我必须在n * n网格中找到最大的正方形。 e.g。

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

最大的方块在底角是3乘3。 我应该在返回之前返回有人可以采取的最多步骤,这样他们就可以无限地重复这个步骤,而不会撞到墙“#”或走出n * n方形,这就是为什么输出比宽度/长度小一个的原因。广场。

我的代码从左到右,从上到下遍历网格,寻找面向下和向右的顶点。一旦找到它,然后找到面向上和向右的最大可能顶点,当它发现检查所有四个边以查看它们是否被组成或.时。在n = 100左右的任何事情上,这个代码在1秒内工作,但是我需要它在1秒内运行n = 500.有关如何加快速度的提示吗?

import sys
input = sys.stdin.readline

n = int(input())
maze = [list(input()) for _ in range(n)]

squares = 0
for r in range(n - 1):
    for c in range(n - 1):
        if maze[r][c] == '.' and maze[r][c + 1] == '.' and maze[r + 1]        [c] == '.':
            sides = []
            for i in range(min(n - r - 1, n - c - 1), -1, -1):
                if maze[r + i][c + i] == '.' and maze[r + i][c + i - 1] == '.' and maze[r + i - 1][c + i] == '.':
                    sides = i
                    if maze[r][c : c + sides] == ['.'] * sides and maze[r + sides][c : c + sides] == ['.'] * sides:
                        a = True
                        for j in range(sides):
                            if maze[r + j][c] != '.' or maze[r + j][c + sides] != '.':
                                a = False
                        if a and sides > squares:
                            squares = sides
                            break
            if squares == n - 1:
                break
print(squares)

3 个答案:

答案 0 :(得分:2)

我可以想到O(n^3)算法如下:

  1. 预计算4个数组:top[][], bottom[][], left[][], right[][],每个数组都存储了(i,j)
  2. 的最大方向长度
  3. 对于每个(i,j),将其用作方形的左下角,对于每个对角点(i-1, j+1), (i-2, j+2) ...等,测试这些点是否可以用作方形& #39; s右上角。存储过程中的最大方形边
  4. 对于步骤1,可以在O(n^2)

    中预先计算所有4个阵列

    对于第2步,当我们遍历所有(i,j)时,对于每个(i,j),我们必须至多看到所有对角点最多n,我们得到的总数O(n^3)

    步骤2中的测试可以使用4个预先计算的阵列在O(1)中完成,只需检查"可能的正方形"的四个角。可以通过检查相应的方向(顶部,底部,左侧,右侧)来加入

    当然,有许多小事可以加快,例如:

    在第2步中,对于每个(i,j),仅检查范围为[current_maximum_diagonal_found ... max(right[i][j], top[i][j])]

    的对角点

    沿整个算法更新current_maximum_diagonal_found,以便我们希望有一些(i,j),我们无需检查整个n对角点。

    但严格来说,它仍然是O(n^3),但就我所知,它应该可以在1秒内运行n~500

答案 1 :(得分:1)

这是一个有趣的问题。我试了一些东西,最后得到了O(n^3)的实现。我对代码进行了评论,以便您可以按照这个想法进行操作。仍然有提高速度的空间,但这个版本已经完成了工作(例如迷宫尺寸为500x500):

Finished after 0.708 seconds.
Result: 112581 squares found, maximum square (x=13, y=270, size=18).

这是源代码(Python 3):

import random
import pprint
import time

# small sample maze
maze = ['.....',
        '...#.',
        '.#...',
        '.#.#.',
        '.#...']
# convert to boolean maze
maze_bin = [[True if cell == '.' else False for cell in line] for line in maze]

# uncomment to generate a random maze
# maze_size = 500
# threshold = 0.2
# maze_bin = [[1 if random.random() >= threshold else 0 for _ in range(maze_size)] for _ in range(maze_size)]

# take start time
t1 = time.time()

# rotate the maze (first column becomes first row, first row becomes first column)
maze_bin_rot = [[maze_bin[i][j] for i in range(len(maze_bin))] for j in range(len(maze_bin[0]))]

# horizontal_lengths is a two-dimensional list that contains the number of possible steps to the right for every cell.
horizontal_lengths = []
for line in maze_bin:
    num = 0
    line_lengths = []
    for i in reversed(line):
        line_lengths.append(i*num)
        num = i * (num + i)
    horizontal_lengths.append(tuple(reversed(line_lengths)))

# vertical_lengths is a two-dimensional list that contains the number of possible steps to the bottom for every cell.
vertical_lengths_rot = []
for line in maze_bin_rot:
    num = 0
    line_lengths = []
    for i in reversed(line):
        line_lengths.append(i*num)
        num = i * (num + i)
    vertical_lengths_rot.append(tuple(reversed(line_lengths)))
# do the rotation again to be back in normal coordinates
vertical_lengths = [[vertical_lengths_rot[i][j] for i in range(len(vertical_lengths_rot))] for j in range(len(vertical_lengths_rot[0]))]

# calculate the maximum size of a square that has it's upper left corner at (x, y).
# this is the minimum of the possible steps to the right and to the bottom.
max_possible_square = []
for y in range(len(maze_bin)):
    line = []
    for x in range(len(maze_bin[0])):
        line.append(min(horizontal_lengths[y][x], vertical_lengths[y][x]))
    max_possible_square.append(line)

# search for squares
results = []
max_size_square = (-1, -1, -1)
for y in range(len(max_possible_square)):
    for x in range(len(max_possible_square[0])):
        # start with maximum possible size and decrease size until a square is found.
        for size in reversed(range(1, max_possible_square[y][x]+1)):
            # look at the upper right (x+size,y) and bottom left corner (x,y+size).
            # if it's possible to make at least size steps to the right from the bottom left corner
            # and at least size steps to the bottom from the upper right corner, this is a valid square.
            if horizontal_lengths[y+size][x] >= size and vertical_lengths[y][x+size] >= size:
                results.append((x, y, size+1))
                if size+1 > max_size_square[2]:
                    max_size_square = (x, y, size+1)
                # break after the the largest square with upper left corner (x,y) has been found.
                break

t2 = time.time()

# comment this print section if you use larger grids
print('Maze:')
pprint.pprint(maze_bin)
print('\n')
print('Horizontal possible steps:')
pprint.pprint(horizontal_lengths)
print('\n')
print('Vertical possible steps:')
pprint.pprint(vertical_lengths)
print('\n')
print('Maximum possible size of square:')
pprint.pprint(max_possible_square)
print('\n')
print('Results:')
for square in results:
    print('Square: x={}, y={}, size={}'.format(*square))
print('\n')

# final results
print('Finished after {:.3f} seconds.'.format(t2-t1))
print('Result: {} squares found, maximum square (x={}, y={}, size={}).'.format(len(results), *max_size_square))

我希望这就是你要找的东西。如果您有任何疑问,请在下面留言;)

答案 2 :(得分:0)

如果我们不想列举所有结果,可能值得考虑的一个优化如下。这是基于策略 - “如果不能导致最佳解决方案,请不要继续使用此单元格”

for y in range(possible_y_value):
    for x in range(possible_x_value):
        # We are ready to process cell identified by (x,y).
        # Check if max_possible_square_length at this cell is greater than size of best_result seen so far. If so, proceed further, otherwise skip this cell
        if max_possible_square[y][x]+1 > best_result.size:
            # proceed further with the inner most for loop
            ....

即使是在最内部的for循环中,当它低于目前为止看到的best_result的大小时,我们可以在迭代时突破循环