Largest Empty Interval

程度★ 難度★★

問題描述(離散版本)

一條陣列,有些格子已被放上障礙物。最長的、連續的空白格子在哪裡?

Recurrence

length(i) =
 { 0                 , if i < 0                    [Exterior]
 { 0                 , if i = 0 and array[i] = 0   [Initial]
 { 1                 , if i = 0 and array[i] = 1   [Initial]
 { 0                 , if i > 0 and array[i] = 0   [Compute]
 { length(i-1) + 1   , if i > 0 and array[i] = 1   [Compute]

length(i):以第i格作為最右端的連續空白的長度。
array[i]:障礙物為0,空白為1。

複雜度

時間複雜度為O(N),空間複雜度為O(N),N為陣列長度。

如果只想計算一個特定問題的答案,那麼空間複雜度可以精簡成O(1),這個部份就不多提了,交給各位來處理。

程式碼:求出最長空白的長度

程式碼:求出最長空白的長度

為了讓程式碼更清爽,這裡把array[]、length[]裡面的數值都往右移動一格,如此就可以省略掉第零格的判斷式,也避免了length[]會溢出邊界。

為了讓程式碼更清爽,這裡也把length[]都初始化為0,如此就不必特別處理array[i] == 0的情況了,相當巧妙。

這兩個技巧是經常使用的的實作技巧,不僅簡化了程式碼的結構,也增加了程式的效率。一定要學會!

程式碼:求出最長空白的位置

求出最長空白的長度之後,在最後加上一段程式碼就可以了。當然可以再改進,就交給各位了。

程式碼:求出其中一個最長空白的位置

也有人會一邊計算表格,一邊紀錄最大值。這種寫法也是很好的,不過只能求出其中一個最長空白的位置。

如果只需要求出隨便一種最長空白的位置,那麼這種寫法就非常適合。

Largest Empty Rectangle

程度★ 難度★★

問題描述(離散版本)

一張方格紙,有許多格子填上了黑色。請找出不包含黑格子的矩形,並且令矩形面積盡量大。

矩形的頂點,可以直接想做是一整個格子,而不是想做直線與橫線的交叉點。

UVa 10074 10502 10667

如果使用窮舉法

最簡單的方法就是用窮舉法。矩形共有四個頂點,只要窮舉所有可能的頂點位置,就可以找出答案來。紙的長寬為H和W的話,共有H*W個位置可以放上頂點;要窮舉所有矩形,時間複雜度就是O((H*W)^4)。另外還要確定矩形內部有沒有包含黑格子,時間複雜度就變成了O((H*W)^5)。

要確定一個矩形的大小和位置,其實只要對角線的兩個頂點就夠了;要窮舉所有矩形,時間複雜度是O((H*W)^2)。確定矩形內部有沒有包含黑格子,就是O((H*W)^3)。

要確定一個矩形的大小和位置,也可以利用矩形左上角的頂點、長、寬;要窮舉所有矩形,時間複雜度是O((H*W)^2)。確定矩形內部有沒有包含黑格子,就是O((H*W)^3)。

談了一堆簡單的做法後,接著來試試Dynamic Programming吧!

嘗試切成條狀,Divide and Conquer

因為原來的紙張又大又複雜,計算面積非常麻煩,所以我們可以試著把紙張切成小塊小塊,逐一處理。這裡將紙張切成橫條狀(這個想法跟積分運算的道理是相同的),並套用上一篇文章所提到的Largest Empty Interval來計算每一條橫條的面積;接著將所有橫條合併起來,便能求出總面積。

將紙張切成橫條狀,此即Divide;每個橫條用Largest Empty Interval來計算面積,此即Conquer;將所有橫條合併,此即Merge。接著來看看要怎麼找出Largest Empty Rectangle吧!

演算法

首先將紙張切成橫條狀,針對每一橫條,找出其中每個點往左可延伸的長度,即是在尋找Largest Empty Interval。

對紙張上的每個位置,都嘗試作為矩形右下角的頂點位置(窮舉所有矩形右下角的位置)。固定矩形右下角的頂點後,觀察該處以上的每個橫條(窮舉所有矩形高度),往左可延伸的長度,便可以求得最大矩形面積。

程式碼

為了讓邊界計算不會溢位,於是將紙張的外面多圍一圈。這是實作二維地圖時很常用的方法。

程式碼

先設計出計算一個橫條的程式碼──計算Largest Empty Interval,運用了DP。(這段程式碼在計算width時,每一格都會覆蓋掉而不受舊值影響,故重算時不必重新初始化。)

補足程式碼,計算所有橫條。

程式碼

對紙張上的每個位置,都嘗試作為矩形右下角的頂點位置。固定矩形右下角的頂點後,觀察該處以上的每個橫條,往左可延伸的長度,便可以求得最大矩形面積。

先設計出計算一個位置的程式碼。

判斷矩形太窄的情形。

補足程式碼,窮舉紙張上所有位置。

複雜度

時間複雜度分析:首先計算了每個橫條的Largest Empty Interval,接著窮舉矩形的右下角頂點位置,又窮舉了矩形的各種高度,算出最大矩形面積。時間複雜度是O((H*W)*H)。

空間複雜度分析:儲存全部問題的答案,空間複雜度是O(H*W)。只想計算一個特定問題的答案,空間複雜度當然可以精簡,這裡就不多提了。

計算的方向是可以改變的。可以改為切直條,可以改為窮舉矩形右上角頂點,道理都一樣。

Largest Empty Rectangle

程度★ 難度★★★

更好的方法

前面介紹的方法用了很多窮舉,也重複計算了很多地方。所以,還可以更快。

這個方法是窮舉紙張上每一個位置,每個位置都去計算以該點為長方形底部,往上延伸到底後,再往左右延伸到底的面積。

如果窮舉一個橫條上的所有位置,便可以得到以該橫條為長方形底部的Largest Empty Rectangle。

所以,只要窮舉紙張上每個位置,就可以算出Largest Empty Rectangle了。

討論

之前只將長方形往左延伸,故要窮舉所有高度。現在改為同時往左右延伸,由於這種延伸方式可得到最大的矩形,便不必窮舉所有高度。

時間複雜度

時間複雜度是O(H*W)。

程式碼:Largest Empty Rectangle的面積

計算過程滿繁複的。大抵上和上一篇的方式差不多,我有點懶的說明,所以直接給程式碼吧。(懶散是不好的行為,請勿模仿。)

程式碼:Largest Empty Rectangle的位置

每當產生最大值之後,就看看此時長方形往上、往左、往右可延伸的距離,就能推敲出最大的長方形的位置。不過這種方式只能找出其中一個長方形的位置。

【待補程式碼】

Largest Empty Rectangle

程度★ 難度★★★

最好的方法

最簡潔的做法,是利用stack,宛如判斷括號對稱一般,將長方形的左右兩邊線找出來。特別要小心的地方,是當stack的元素全部彈出之後,之後出現的右邊線還是有用處的,不能把它想做是孤單的右括號。時間複雜度是O(H*W)。

1. 切成直條。預先用DP計算每一條直線的Empty Interval高度。
2. 窮舉每一個橫條,作為長方形的底線,並利用stack算出最大矩形。

演練其中一段過程:

1.
好啦,就拿這筆測資來說明吧XD。

   0000000000000000
   0000011111000000
   0011111111100000
   0111111111110000
   1111111111110000
   1111111111111111
   0000000000000000

2.
首先呢,請先用DP求出每一縱列的重複次數。

   0000000000000000
   0000011111000000
   0011122222100000
   0122233333210000
   1233344444320000
   2344455555431111
   0000000000000000

3.
接下來,讓我們用堆疊來輔助計算。
先引入一個結論,堆疊中的值「從下到上」必須是遞增的。
此外,為了方便起見,我們直接從倒數第二行開始執行。

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   1233344444320000       |             |
-> 2344455555431111       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
4-1.
首先碰到的第一個高度是「高度2」,把它放入堆疊。

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   ^233344444320000       |             |
   ^344455555431111       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-2.
碰到「高度3」,「高度3」 > 「高度2」,因此把「高度3」放入堆疊。

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |
   0^22233333210000       +-------------+
   1^33344444320000       |             |
   2^44455555431111       +-------------+
   0000000000000000       |             |
                    ESP-> +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-3.

   0000000000000000
   0000011111000000       +-------------+
   00^1122222100000       |             |
   01^2233333210000       +-------------+
   12^3344444320000       |             |
   23^4455555431111 ESP-> +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-4.

   0000000000000000
   0000011111000000       +-------------+
   001^122222100000       |             |
   012^233333210000       +-------------+
   123^344444320000       |             |
   234^455555431111 ESP-> +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-5.

   0000000000000000
   0000011111000000       +-------------+
   0011^22222100000       |             |
   0122^33333210000       +-------------+
   1233^44444320000       |             |
   2344^55555431111 ESP-> +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-6.
唷呵,又碰到一個比「高度4」大的了,把「高度5」放入堆疊吧!

   0000000000000000
   00000^1111000000       +-------------+
   00111^2222100000       |             |
   01222^3333210000 ESP-> +-------------+
   12333^4444320000       | 高度5  位置6 |
   23444^5555431111       +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-7.

   0000000000000000
   000001^111000000       +-------------+
   001112^222100000       |             |
   012223^333210000 ESP-> +-------------+
   123334^444320000       | 高度5  位置6 |
   234445^555431111       +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-8.

   0000000000000000
   0000011^11000000       +-------------+
   0011122^22100000       |             |
   0122233^33210000 ESP-> +-------------+
   1233344^44320000       | 高度5  位置6 |
   2344455^55431111       +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-9.

   0000000000000000
   00000111^1000000       +-------------+
   00111222^2100000       |             |
   01222333^3210000 ESP-> +-------------+
   12333444^4320000       | 高度5  位置6 |
   23444555^5431111       +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-10.

   0000000000000000
   000001111^000000       +-------------+
   001112222^100000       |             |
   012223333^210000 ESP-> +-------------+
   123334444^320000       | 高度5  位置6 |
   234445555^431111       +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-11.
咦?現在碰到的「高度4」比堆疊頂的「高度5」小了。
換句話說,高度是5的矩形已經到了「盡頭」。
把「高度5」給pop出來順便計算面積吧!
area = 高度5 * (位置11 - 位置6) = 25

   0000000000000000         高度5  位置6 
   00000∎∎∎∎∎000000       +-------------+
   00111∎∎∎∎∎^00000       |             |
   01222∎∎∎∎∎^10000       +-------------+
   12333∎∎∎∎∎^20000       |             |
   23444∎∎∎∎∎^31111 ESP-> +-------------+
   0000000000000000       | 高度4  位置3 |
                          +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-12.
啊哈!又碰到一個比堆疊頂的小的了!
pop出來並計算面積吧!
area = 高度4 * (位置12 - 位置3) = 36

   0000000000000000         高度4  位置3
   0000011111000000       +-------------+
   00∎∎∎∎∎∎∎∎∎00000       |             |
   01∎∎∎∎∎∎∎∎∎^0000       +-------------+
   12∎∎∎∎∎∎∎∎∎^0000       |             |
   23∎∎∎∎∎∎∎∎∎^1111       +-------------+
   0000000000000000       |             |
                    ESP-> +-------------+
                          | 高度3  位置2 |
                          +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-13-1.
再度碰到一個比堆疊頂的小的了!
area = 高度3 * (位置13 - 位置2) = 33

   0000000000000000         高度3  位置2
   0000011111000000       +-------------+
   0011122222100000       |             |
   0∎∎∎∎∎∎∎∎∎∎∎0000       +-------------+
   1∎∎∎∎∎∎∎∎∎∎∎0000       |             |
   2∎∎∎∎∎∎∎∎∎∎∎^111       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
                          | 高度2  位置1 |
                          +-------------+
4-13-2.
目前「高度1」還是比堆疊頂的「高度2」小,
所以還要把「高度1」pop出來!
area = 高度2 * (位置13 - 位置1) = 24

   0000000000000000         高度2  位置1
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   ∎∎∎∎∎∎∎∎∎∎∎∎0000       |             |
   ∎∎∎∎∎∎∎∎∎∎∎∎^111       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
4-13-3.
嗯,所以呢?
你是不是忘了要把「高度1」放入堆疊啊XDDDDDD
堆疊現在是空的呀(或者說,「高度1」比目前堆疊頂還大?)

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |  啊啊 這裡要特別
   0122233333210000       +-------------+  注意一下,因為我
   1233344444320000       |             |  們在判斷 pop的時
   234445555543^111       +-------------+  候都是 堆疊頂>目
   0000000000000000       |             |  前高度,所以最後
                          +-------------+  再push進去的位置
                          |             |  要是「最後一個p-
                    ESP-> +-------------+  op出來的位置。」
                          | 高度1 (位置1)|
                          +-------------+
4-14.

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   1233344444320000       |             |
   2344455555431^11       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
                          | 高度1  位置1 |
                          +-------------+
4-15.

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   1233344444320000       |             |
   23444555554311^1       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
                          | 高度1  位置1 |
                          +-------------+
4-16.
呼!終於走到底了!

   0000000000000000
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   1233344444320000       |             |
   234445555543111^       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+
                          | 高度1  位置1 |
                          +-------------+
5.
最後呀,記得把stack當中的全部pop出來喔!
area = 高度1 * (位置17 - 位置1) = 16

   0000000000000000         高度1  位置1
   0000011111000000       +-------------+
   0011122222100000       |             |
   0122233333210000       +-------------+
   1233344444320000       |             |
   ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎       +-------------+
   0000000000000000       |             |
                          +-------------+
                          |             |
                          +-------------+
                          |             |
                    ESP-> +-------------+

Largest Empty Square

程度★ 難度★★

問題描述(離散版本)

跟Largest Empty Rectangle類似,只是改為找正方形而已。

Recurrence

area(i, j) =
 { 0                   , if i < 0 or j < 0       [Exterior]
 {
 { min(                , if i >= 0 and j >= 0    [Compute]
 {    area(i-1, j),      and (i, j) is empty
 {    area(i, j-1),
 {    area(i-1, j-1)
 { ) + 1 
 {
 { 0                   , if i >= 0 and j >= 0    [Compute]
 {                       and (i, j) is blocked

area(i, j):右下角頂點為(i, j)的最大正方形的面積。

重點在於:

area(i, j) = min( area(i-1, j), area(i, j-1), area(i-1, j-1) ) + 1

複雜度

時間複雜度為O(H*W),空間複雜度為O(min(H, W))。

程式碼

UVa 10908