2016-03-08 30 views
2

我有一個問題,我必須在n * n網格中找到最大的方形。 例如尋找n * n網格中最大的方形

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

其中最大的廣場將在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) 
+0

O(n^3)算法如何?我認爲它仍然可以在1秒內適合n = 500 – shole

+1

爲什麼不是右上角的3x3方格? – MSeifert

+0

無論是會工作,我不得不編輯示例,使其格式正確,沒有看到。 – Yunis

回答

1

這是一個有趣的問題。我嘗試了一些東西,最終實現了這個實現,即O(n^3)。我評論了這些代碼,希望你能按照這個想法去做。速度還有提升空間,但這個版本已經可以完成這項工作(例如,與迷宮尺寸500×500):

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

我能想到一個O(n^3)算法如下:

  1. 預先計算4數組:top[][], bottom[][], left[][], right[][],每個商店的方向的最大長度可以從(i,j)
  2. 去對於每個(i,j),用它作爲正方形的左下角,對於它的每個對角點(i-1, j+1), (i-2, j+2) ...等,測試這些點是否可以用作正方形的右上角。存儲的最大正方形側的過程中

對於步驟1中,所有4個陣列可以在O(n^2)

預先計算對於步驟2中,如我們遍歷所有(i,j),並且對於每個(i,j),我們必須看到在最重要的對角點,這是他們在最n,我們總共得到O(n^3)

在步驟2中的測試可以在O(1)使用4個預先計算陣列來完成,只需檢查的「可能的正方形」的4個角可以通過檢查相應的方向加入(頂部,底部,左,右)


當然,有許多小的東西可以做加快,例如:

在步驟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

+0

好吧,我有同樣的想法(請參閱我的文章),但實際上它有足夠的兩個預先計算的數組。例如,如果你從左上角開始,你只需要知道你可以從每個點到右邊和底部多遠。 – Felix

+0

是啊,你是絕對正確的,我只是粗略地提供了一個簡單的想法,沒有想太多... – shole

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尺寸時