1. 程式人生 > >淺談用極大化思想解決最大子矩形問題

淺談用極大化思想解決最大子矩形問題

通過前面兩步,可以枚舉出所有的極大子矩形。演算法1的時間複雜度是O(S2)。這樣,可以解決大多數最大子矩形和相關問題了。

雖然以上的演算法(演算法1)看起來是比較高效的,但也有使用的侷限性。可以發現,這個演算法的複雜度只與障礙點的個數s有關。但對於某些問題,s最大有可能達到n×m,當s較大時,這個演算法就未必能滿足時間上的要求了。能否設計出一種依賴於n和m的演算法呢?這樣在演算法1不能奏效的時候我們還有別的選擇。我們再重新從最基本的問題開始研究。

演算法2

      首先,根據定理1:最大有效子矩形一定是一個極大子矩形。不過與前一種演算法不同的是,我們不再要求每一次列舉的一定是極大子矩形而只要求所有的極大子矩形都被列舉到。看起來這種演算法可能比前一種差,其實不然,因為前一種演算法並不是完美的:雖然每次考察的都是極大子矩形,但它還是做了一定量的“無用功”。可以發現,當障礙點很密集的時候,前一種演算法會做大量沒用的比較工作。要解決這個問題,我們必須跳出前面的思路,重新考慮一個新的演算法。注意到極大子矩形的個數不會超過矩形內單位方格的個數,因此我們有可能找出一種時間複雜度是O(N×M)的演算法。

 定義:

有效豎線:除了兩個端點外,不覆蓋任何障礙點的豎直線段。

懸線:上端點覆蓋了一個障礙點或達到整個矩形上端的有效豎線。如圖所示的三個有效豎線都是懸線。

      對於任何一個極大子矩形,它的上邊界上要麼有一個障礙點,要麼和整個矩形的上邊界重合。那麼如果把一個極大子矩形按x座標不同切割成多個(實際上是無數個)與y軸垂直的線段,則其中一定存在一條懸線。而且一條懸線通過儘可能地向左右移動恰好能得到一個子矩形(未必是極大子矩形,但只可能向下擴充套件)。通過以上的分析,我們可以得到一個重要的定理。

定理3】:如果將一個懸線向左右兩個方向儘可能移動所得到的有效子矩形稱為這個懸線所對應的子矩形,那麼所有懸線所對應的有效子矩形的集合一定包含了所有極大子矩形的集合。

定理3中的“儘可能”移動指的是移動到一個障礙點或者矩形邊界的位置。

根據【定理3】可以發現,通過列舉所有的懸線,就可以枚舉出所有的極大子矩形。由於每個懸線都與它底部的那個點一一對應,所以懸線的個數=(n-1)×m(以矩形中除了頂部的點以外的每個點為底部,都可以得到一個懸線,且沒有遺漏)。如果能做到對每個懸線的操作時間都為O(1),那麼整個演算法的複雜度就是O(NM)。這樣,我們看到了解決問題的希望。

      現在的問題是,怎樣在O(1)的時間內完成對每個懸線的操作。我們知道,每個極大子矩形都可以通過一個懸線左右平移得到。所以,對於每個確定了底部的懸線,我們需要知道有關於它的三個量:頂部、左右最多能移動到的位置。對於底部為(i,j)的懸線,設它的高為hight[i,j],左右最多能移動到的位置為left[i,j],right[i,j]。為了充分利用以前得到的資訊,我們將這三個函式用遞推的形式給出。

  對於以點(i,j)為底部的懸線:

如果點(i-1,j)為障礙點,那麼,顯然以(i,j)為底的懸線高度為1,而且左右均可以移動到整個矩形的左右邊界,即

 height[i,j]=l

left[i,j]=0

right[i,j]=m

如果點(i-1,j)不是障礙點,那麼,以(i,j)為底的懸線就等於以(i-1,j)為底的懸線+點(i,j)到點(i-1,j)的線段。因此,height[i,j]=height[i-1,j]+1。比較麻煩的是左右邊界,先考慮left[i,j]。如下圖所示,(i,j)對應的懸線左右能移動的位置要在(i-1,j)的基礎上變化。

即left[i,j]=max( left[i-1,j] , (i-1,j)左邊第一個障礙點位置 ) 如圖

   

 right[i,j]的求法類似。綜合起來,可以得到這三個引數的遞推式:

height[i,j]=height[i-1,j]+1

left[i,j]=max( left[i-1,j] , (i-1,j)左邊第一個障礙點位置,邊界0也是障礙點 )

right[i,j]=max( right[i-1,j] , (i-1,j)右邊第一個障礙點位置,邊界m也是障礙點 )

   這樣做充分利用了以前得到的資訊,使每個懸線的處理時間複雜度為O(1)。對於以點(i,j)為底的懸線對應的子矩形,它的面積為(right[i,j]-left[i,j])*height[i,j]。

這樣最後問題的解就是:

Result=max(right[i,j]-left[i,j])*height[i,j] (l<=i<n, l<=j<=m)

整個演算法的時間複雜度為O(NM),空間複雜度是O(NM)。

  兩個演算法的對比:

以上說了兩種具有一定通用性的處理演算法,時間複雜度分別為O(S2)和O(NM)。兩種演算法分別適用於不同的情況。從時間複雜度上來看,第一種演算法對於障礙點稀疏的情況比較有效,第二種演算法則與障礙點個數的多少沒有直接的關係(當然,障礙點較少時可以通過對障礙點座標的離散化來減小處理矩形的面積,不過這樣比較麻煩,不如第一種演算法好),適用於障礙點密集的情況。

五、   例題

將前面提出的兩種演算法運用於具體的問題。

1、    Winter Camp2002,奶牛浴場

分析

題目的數學模型就是給出一個矩形和矩形中的一些障礙點,要求出矩形內的最大有效子矩形。這正是我們前面所討論的最大子矩形問題,因此前兩種演算法都適用於這個問題。

下面分析兩種演算法運用在本題上的優略:

對於第一種演算法,不用加任何的修改就可以直接應用在這道題上,時間複雜度為O(S2),S為障礙點個數;空間複雜度為O(S)。

對於第二種演算法,需要先做一定的預處理。由於第二種演算法複雜度與牛場的面積有關,而題目中牛場的面積很大(30000×30000),因此需要對資料進行離散化處理。離散化後矩形的大小降為S×S,所以時間複雜度為O(S2),空間複雜度為O(S)。說明:需要注意的是,為了保證演算法能正確執行,在離散化的時候需要加上S個點,因此實際需要的時間和空間較大,而且程式設計較複雜。

從以上的分析來看,無論從時空效率還是程式設計複雜度的角度來看,這道題採用第一種演算法都更優秀。

2、    OIBH模擬賽1,提高組,Candy

題意簡述:(原題見論文附件)

一個被分為 n*m 個格子的糖果盒,第 i 行第 j 列位置的格子裡面有 a[i,j] 顆糖。但糖果盒的一些格子被老鼠洗劫。現在需要儘快從這個糖果盒裡面切割出一個矩形糖果盒,新的糖果盒不能有洞,並且希望保留在新糖果盒內的糖的總數儘量多。

引數約定:1 ≤ n,m ≤ 1000

分析

首先需要注意的是:本題的模型是一個矩陣,而不是矩形。在矩陣的情況下,由於點的個數是有限的,所以又產生了一個新的問題:最大權值子矩陣

   定義:

   有效子矩陣為內部不包含任何障礙點的子矩形。與有效子矩形不同,有效子矩陣地邊界上也不能包含障礙點。

有效子矩陣的權值(只有有效子矩形才有權值)為這個子矩陣包含的所有點的權值和。

最大權值有效子矩陣為所有有效子矩陣中權值最大的一個。以下簡稱為最大權值子矩陣

本題的數學模型就是正權值條件下的最大權值子矩陣問題。再一次利用極大化思想,因為矩陣中的權值都是正的,所以最大權值子矩陣一定是一個極大子矩陣。所以我們只需要列舉所有的極大子矩陣,就能從中找到最大權值子矩陣。同樣,兩種演算法只需稍加修改就可以解決本題。下面分析兩種演算法應用在本題上的優略:

對於第一種演算法,由於矩形中障礙點的個數是不確定的,而且最大有可能達到N×M,這樣時間複雜度有可能達到O(N2M2),空間複雜度為O(NM)。此外,由於矩形與矩陣的不同,所以在處理上會有一些小麻煩。

對於第二種演算法,稍加變換就可以直接使用,時間複雜度為O(NM),空間複雜度為O(NM)。

可以看出,第一種演算法並不適合這道題,因此最好還是採用第二種演算法。

3、    Usaco Training, Section 1.5.4, BigBarn

題意簡述(原題見論文附件)

      Farmer John想在他的正方形農場上建一個正方形穀倉。由於農場上有一些樹,而且Farmer John又不想砍這些樹,因此要找出最大的一個不包含任何樹的一塊正方形場地。每棵樹都可以看成一個點。

引數約定:牛場為N×N的,樹的棵數為T。N≤1000,T≤10000。

分析

    這題是矩形上的問題,但要求的是最大子正方形。首先,明確一些概念。

1、定義有效子正方形為內部不包含任何障礙點的子正方形

2、定義極大有效子正方形為不能再向外擴充套件的有效子正方形,一下簡稱極大子正方形

3、定義最大有效子正方形為所有有效子正方形中最大的一個(或多個),以下簡稱最大子正方形

本題的模型有一些特殊,要在一個含有一些障礙點的矩形中求最大子正方形。這與前兩題的模型是否有相似之處呢?還是從最大子正方形的本質開始分析。

與前面的情況類似,利用極大化思想,我們可以得到一個定理:

定理4】:在一個有障礙點的矩形中的最大有效子正方形一定是一個極大有效子正方形。

             根據【定理4】,我們只需要枚舉出所有的極大子正方形,就可以從中找出最大子正方形。極大子正方形有什麼特徵呢?所謂極大,就是不能再向外擴充套件。如果是極大子矩形,那麼不能再向外擴充套件的充要條件是四條邊上都覆蓋了障礙點(【定理2】)。類似的,我們可以知道,一個有效子正方形是極大子正方形的充要條件是它任何兩條相鄰的邊上都覆蓋了至少一個障礙點。根據這一點,可以得到一個重要的定理。

定理5】:每一個極大子正方形都至少被一個極大子矩形包含。且這個極大子正方形一定有兩條不相鄰的邊與這個包含它的極大子矩形的邊重合。

根據【定理5】,我們只需要列舉所有的極大子矩形,並檢查它所包含的極大子正方形(一個極大子矩形包含的極大子正方形都是一樣大的)是否是最大的就可以了。這樣,問題的實質和前面所說的最大子矩形問題是一樣的,同樣的,所採用的演算法也是一樣的。

因為演算法1和演算法2都枚舉出了所有的極大子矩形,因此,演算法1和演算法2都可以用在本題上。具體的處理方法如下:對於每一個枚舉出的極大子矩形,如圖所示,如果它的邊長為a、b,那麼它包含的極大子正方形的邊長即為min(a,b)。

考慮到N和T的大小不同,所以不同的演算法會有不同的效果。下面分析兩種演算法應用在本題上的優略。

對於第一種演算法,時間複雜度為O(T2),對於第二種演算法,時間複雜度為O(N2)。因為N<T,所以從時間複雜度的角度看,第二種演算法要比第一種演算法好。考慮到兩個演算法的空間複雜度都可以承受,所以選擇第二種演算法較好些。

以下是第一種和第二種演算法程式設計實現後在USACO Training Program Gateway上的執行時間。可以看出,在資料較大時,演算法2的效率比演算法1高。

演算法1:

Test  1:  0.009375

Test  2:  0.009375

Test  3:  0.009375

Test  4:  0.009375

Test  5:  0.009375

Test  6:  0.009375

Test  7:  0.021875

Test  8:     0.025

Test  9:  0.084375

Test 10:    0.3875

Test 11:     0.525

Test 12:    0.5625

Test 13:  0.690625

Test 14:   0.71875

Test 15:      0.75

演算法2:

Test  1:  0.009375

Test  2:  0.009375

Test  3:  0.009375

Test  4:  0.009375

Test  5:  0.009375

Test  6:   0.00625

Test  7:  0.009375

Test  8:  0.009375

Test  9:    0.0125

Test 10:  0.021875

Test 11:  0.028125

Test 12:   0.03125

Test 13:   0.03125

Test 14:   0.03125

Test 15:  0.034375

以上,利用極大化思想和前面設計的兩個演算法,通過轉換模型,解決了三個具有一定代表性的例題。解題的關鍵就是如何利用極大化思想進行模型轉換和如何選擇演算法。

 六、小結

     設計演算法要從問題的基本特徵入手,找出解題的突破口。本文介紹了兩種適用於大部分最大子矩形問題及相關變型問題的演算法,它們設計的突破口就是利用了極大化思想,找到了列舉極大子矩形這種方法。

在效率上,兩種演算法對於不同的情況各有千秋。一個是針對障礙點來設計的,因此複雜度與障礙點有關;另一個是針對整個矩形來設計的,因此複雜度與矩形的面積有關。雖然兩個演算法看起來有著巨大的差別,但他們的本質是相通的,都是利用極大化思想,從列舉所有的極大有效子矩形入手,找出解決問題的方法。

 需要注意的是,在解決實際問題是僅靠套用一些現有演算法是不夠的,還需要對問題進行全面、透徹的分析,找出解題的突破口。

此外,如果採用極大化思想,前面提到的兩種演算法的複雜度已經不能再降低了,因為極大有效子矩形的個數就是O(NM)或O(S2)的。如果採用其他演算法,理論上是有可能進一步提高演算法效率,降低複雜度的。 

七、 附錄:

說明:所有程式均在Free Pascal IDE for Dos, Version  0.9.2上編譯執行

參考書目

1、     資訊學奧林匹克競賽指導

                         ----1997~1998競賽試題解析

            吳文虎 王建德 著

2、     IOI99中國集訓隊優秀論文集

3、     資訊學奧林匹克(季刊)

4、     《金牌之路 競賽輔導》 

江文哉主編  陝西師範大學出版社出版

原文: 福州第三中學  王知昆