原文 這題目是經典的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小時內的所有取樣...