接上文:http://blog.csdn.net/jj12345jj198999/article/details/6607832
選定I為所有線段中具有最大左端點值的線段,由於其他線段左端點值都較之小,這樣就不需要再檢查左端點值了。因此,為了要檢查I是否包含於其他線段之中,我們只需要檢查其他線段中是否有右端點值比I右端點值更大的線段。為了找到這麼一條具有最大左端點值的線段我們可以依據線段的左端點值對所有線段進行排序並且按照次序對其進行搜尋。假定線段已經按照次序排好,L1≤L2≤...≤Ln。歸納假設如下:
歸納假設:我們已經知道如何去解決包含I1,I2...In-1條線段的問題。
最基本的問題很簡單,如果只有一條線段,那麼它肯定不會被標記。現在我們來考慮In。通過歸納假設我們知道已經確定了線上段I1到In-1中有哪些線段是包含在其他線段之中的,我們需要確定In是否包含其他(之前沒有被標記過)的線段,以及In是否被包含在其他線段之中。讓我們首先來檢查In是否被包含在其他線段之中。如果In被包含在一條線段中,假如是Ij,j<n,於是Rj≥Rn。這是唯一的必要條件(因為排序已經確保了Lj≤Ln)。因此我們只需要在之前的線段中記錄擁有的最大右端點值即可。把該右端點值與Rn進行比較就能給出我們答案。我們稍微改變一下歸納假設:
更強的歸納假設:我們已經知道如何解決I1,I2...In-1的問題,同時也找出了他們中右端點的最大值。
我們再一次讓In作為擁有最大左端點值的線段,然後讓MaxR作為前n-1條線段中右端點的最大值。如果MaxR≥Rn,那麼In就應該被標記,否則(MaxR≤Rn)Rn就成為新的最大的最大值。(這最後一步是必要的,因為我們現在不僅僅是標記線段,同時我們也在尋找最大的右端點值。)我們現在僅通過一步檢查就能確定In的狀態。
為了完成這個演算法我們需要檢查In是否包含一條之前未被標記過的線段。In包含一條線段Ij,j<n,只有當滿足條件Lj=Ln且Rj<Rn時才行。我們可以通過增強排序功能來處理這種情況。現在我們不僅按照左端點值進行排序,同時對於具有相同左端點值的線段,我們依據他們的右端點值的逆序來進行排序。這樣能夠排除上面提到的情況出現,因為Ij將會放置在In之後(右端點值較小),而且Ij的包含性將會被上一個演算法找到。完整的演算法如下所示:
線段包含性演算法
(I1,I2,...In:線段)
{用一對Lj,Rj來給出一條線段Ij}
{我們假定沒有兩條線段是完全一樣的}
{當Ij被包含在另一條線段中時,Mark[j]將會被標記為真}
begin
按照線段的左端點值遞增次序對所有線段進行排序
具有相同左端點值的線段按照他們右端點值遞減的次序進行排序
{for all j<k either Lj<Lk or Lj=Lk and Rj>Rk}
MaxR:=R1;
for j:=2 to n do
if Rj≤MaxR then
Mark[j]:=true;
else
Mark[j]:=false;
MaxR:=Rj;
end;
複雜性:除了排序外,這個演算法包含一個涉及到O(n)步操作的迴圈。由於排序需要O(nlogn)步操作,故其佔據了演算法主要的已耗用時間。
總結:這個例子闡明了一個不是那麼直接運用歸納法的方法。首先我們選擇可以運用歸納法執行的序列。然後我們設計歸納假設,該假設能夠暗示出期望的結果同時它也易拓展。重點放在這些步驟上就能讓對很多演算法的設計變得簡單。
明智的選擇歸納序列
在前面的例子中,重點都放在尋找一個縮減問題規模的演算法上。這是使用歸納法的本質。然而有很多方法也能實現這一點。首先,問題可能包含一些參數(例中的左端點右端點,頂點和邊),我們必須決定對其中哪一個參數進行縮減。其次,我們可能能夠排除掉許多可能元素,但我們想選擇其中最容易的一個。(例如最左邊的端點,最小的數字)第三,我們可能想對問題加強限制條件(例如線段是有序排列的)。當然還有其他很多變化。例如我們可以假設我們已經知道對一些小於n的值的問題如何解決而不是僅僅的n-1。這往往是一個有效假設。任何縮減問題規模的做法都是值得考慮的,因為這種做法能夠引導我們回到問題的最基本情況,而這時往往我們能夠直接解決。回到在歸納法中討論的排序例子,我們可以把對n個元素的排序縮減為對兩個各含n/2個元素的子集的排序。然後可以對這兩個排好序的集合進行合并(引出一個稱為歸併排序的演算法)。把問題分解(歸納性的)為一些相同的部分是一個很有用的技巧(我們將會在後面進行討論),該技巧稱為分治。
一些問題的縮減很容易就能實現,然而一些卻很難實現。一些規模的縮減能夠帶來很不錯的演算法,然而一些卻不行。在很多情況下這就是問題中唯一的困難之處,一旦做出了正確的選擇那麼剩下的就很容易了(例如在映射問題中元素i的選擇那樣)。這在數學中極其重要。沒有人能夠不通過首先思考如何選擇一個歸納序列就直接跳到歸納證明中去。正如估計的那樣,這在演算法設計中也是很重要的。在這一部分我們將給出兩個例子,在這兩個例子中,縮減序列的重要性得到了體現。特別是名人的例子,它說明了一種不同於一般的順序。