最長不降子串行

2022-05-02 20:27:14 字數 3192 閱讀 3310

原文 這題目是經典的dp題目,也可叫作lis(longest increasing subsequence)最長上公升子串行或者 最長不下降子串行。很基礎的題目,有兩種演算法,複雜度分別為o(n*logn)和o(n^2) 。

一.問題描述

設有由n個不相同的整數組成的數列,記為:

a(1)、a(2)、……、a(n)且a(i)<>a(j) (i<>j)

例如3,18,7,14,10,12,23,41,16,24。

二.演算法分析

(一)。根據動態規劃的原理,由後往前進行搜尋。

1·對a(n)來說,由於它是最後乙個數,所以當從a(n)開始查詢時,只存在長度為1的不下降序列;

2·若從a(n-1)開始查詢,則存在下面的兩種可能性:

①若a(n-1)②若a(n-1)>a(n)則存在長度為1的不下降序列a(n-1)或a(n)。

3·一般若從a(i)開始,此時最長不下降序列應該按下列方法求出:

在a(i+1),a(i+2),…,a(n)中,找出乙個比a(i)大的且最長的不下降序列,作為它的後繼。倒推公式為

f(i)=max+1

f[i]:表示以第i個位置為起點的最長不下降序列的長度。

k的選擇範圍:a(i+k)>a(i)   i+k≦n

最後從f[1]到f[n]中選取最大的即為最優解

當然也可以採用順推的方法,順推公式為

f(i)=max+1

f[i]:表示以第i個位置為終點的最長不下降序列的長度。

k的選擇範圍:a(i-k)最後從f[1]到f[n]中選取最大的即為最優解

4·為演算法上的需要,定義乙個陣列(倒推法)

整數型別二維陣列d(n,3)

d(i,1)表示點a(i)

d(i,2)表示從i位置到達n的最長不下降序列長度

d(i,3)表示從i位置開始最長不下降序列的下乙個數字的位置,以便列印。

初始化:

for i = 1 to n

input #1, d(i, 1)

d(i, 2) = 1

d(i, 3) = 0

next i

#include using namespacestd;

int main(void)

for(max=i=0;imax)

max=b[i];

cout}

return 0}

#include using namespacestd;

int find(int *a,int len,int n)//若返回值為x,則a[x]>=n>a[x-1]

returnleft;

}void fill(int *a,intn)

int main(void)

for(max=i=0;imax)

max=b[i];

cout}

return 0;

}

對於這段程式,我們可以用演算法導論上的loop invariants來幫助理解. loop invariant:

1、每次迴圈結束後c都是單調遞增的。(這一性質決定了可以用二分查詢)

2、每次迴圈後,c[i]總是儲存長度為i的遞增子串行的最末的元素,若長度為i的遞增子串行有多個,剛儲存末尾元素最小的那個.(這一性質決定是第3條性質成立的前提)

3、每次迴圈完後,b[i]總是儲存以a[i]結尾的最長遞增子串行。 initialization:

1、進入迴圈之前,c[0]=-1,c[1]=a[0],c的其他元素均為1000,c是單調遞增的; 

2、進入迴圈之前,c[1]=a[0],儲存了長度為1時的遞增序列的最末的元素,且此時長度為1的遞增了序列只有乙個,c[1]也是最小的; 3、進入迴圈之前,b[0]=1,此時以a[0]結尾的最長遞增子串行的長度為1. maintenance:

1、若在第n次迴圈之前c是單調遞增的,則第n次迴圈時,c的值只在第6行發生變化,而由c進入迴圈前單調遞增及find函式的性質可知(見find的注釋),此時c[j+1]>c[j]>=a[i]>c[j-1],所以把c[j]的值更新為a[i]後,c[j+1]>c[j]>c[j-1]的性質仍然成立,即c仍然是單調遞增的;

2、迴圈中,c的值只在第6行發生變化,由c[j]>=a[i]可知,c[j]更新為a[i]後,c[j]的值只會變小不會變大,因為進入迴圈前c[j]的值是最小的,則迴圈中把c[j]更新為更小的a[i],當然此時c[j]的值仍是最小的; 

3、迴圈中,b[i]的值在第7行發生了變化,因為有loop invariant的性質2,find函式返回值為j有:c[j-1]c[j-1]是小於a[i]的,且以c[j-1]結尾的遞增子串行有最大的長度,即為j-1,把a[i]接在c[j-1]後可得到以a[i]結尾的最長遞增子串行,長度為(j-1)+1=j; termination:迴圈完後,i=n-1,b[0],b[1],...,b[n-1]的值均已求出,即以a[0],a[1],...,a[n-1]結尾的最長遞增子串行的長度均已求出,再通過第8行的迴圈,即求出了整個陣列的最長遞增子串行。

仔細分析上面的**可以發現,每次迴圈結束後,假設已經求出c[1],c[2],c[3],...,c[len]的值,則此時最長遞增子串行的長度為len,因此可以把上面的**更加簡化,即可以不需要陣列b來輔助儲存,第8行的迴圈也可以省略。

#include using namespacestd;

int find(int *a,int len,int n)//修改後的二分查詢,若返回值為x,則a[x]>=n

returnleft;

}int main(void)

cout}

return 0;

}

比較一點的實現:

#include #include using namespacestd;

const int n = 1001;

int a[n], c[n], f[n]; // f[i]用於記錄a[0最長不降子串行i]的最大長度

int bsearch(const int *c, int size, const int &a)

}int lis(const int *a, const int &n)

returnsize;

}int main(void)

return 0;

}

**:

最長不降子串行問題

一 問題 輸入 設有由n個不相同的整數組成的數列,記為 a 1 a 2 a n 且a i a j i j 若存在i1長度為e的不下降序列。輸出 程式要求,當原數列給出之後,求出最長的不下降序列。例子 數列3,18,7,14,10,12,23,41,16,24。3,18,23,24就是乙個長度為4的不...

最長不降子串行 dp

問題描述 給定乙個字串,求出其不降子串行的最大長度。分析 從後往前逆推 06對於n個數,我們可以分為n 1個階段。07我們由後向前搜尋 逆推法 我們可以這樣思考 081 對a n 來說,由於它是最後乙個數,所以當從a n 開始查詢時,只存在長度為1的不下降序列 092 若從a n 1 開始查詢,則存...

最長不降子串行(二)

受到 一 中啟發,現補充位元組跳動19年第二次筆試第四題程式。題目如下 4.一天,小凱同學震驚的發現,自己屋內的pm2.5指標是有規律的!小凱取樣了pm2.5的數值,發現pm2.5數值以小時為週期迴圈,即任意時刻的pm2.5總是和一小時前相等!他的室友小文同學提出了這樣乙個問題,在t小時內的所有取樣...