求陣列中最長遞增子串行 LIS

2021-06-10 12:54:58 字數 2866 閱讀 4974

什麼是最長遞增子串行呢?

問題描述如下:

設l=是n個不同的實數的序列,l的遞增子串行是這樣乙個子串行lin=,其中k1#include

using namespace std;

int main()

for(max=i=0;i求出整個數列的最長遞增子串行的長度

if(b[i]>max)

max=b[i];

cout<

}return 0;

}顯然,這種方法的時間複雜度仍為o(n^2);

3、對第二種思路的改進:

第二種思路在狀態轉移時的複雜度為o(n),即在找a[k]前面滿足a[j]#include

using namespace std;

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

return left;

}void fill(int *a,int n)

int main()

for(max=i=0;i

if(b[i]>max)

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]長度,即為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 namespace std;

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

return left;

}int main()

cout<

}return 0;

}最長遞增部分序列 longest ordered subsequence extention hoj10027 poj2533

2007-08-21 20:19

求最長遞增部分序列是乙個比較常見的動態規劃題。在飛彈攔截等題中都有用到。一般來說就是用經典的o(n^2)的動態規劃演算法。

演算法如下:

設a[i]表示序列中的第i個數,f[i]表示從1到i這一段中以i結尾的最長上公升子串行的長度,初始時設f[i] = 0(i = 1, 2, ..., len(a))。則有動態規劃方程:f[i] = max (j = 1, 2, ..., i - 1, 且a[j] < a[i])。

然而在hoj10027中n的值達到了50000。顯而易見經典演算法是會超時滴。所以只有另謀出路了。

用乙個變數len記錄到目前為止所找出來的最長遞增序列的長度。另外準備乙個陣列b,用這個陣列表示長度為j的遞增序列中最後乙個元素的值。在這裡長度為j的遞增序列不止乙個,我們所要儲存是那個最小的。為什麼呢?因為最後乙個元素越小,那麼這個遞增序列在往後被延長的機會越大。初始化b[0] = -1;len = 0;從第乙個元素a[1]開始       a[i]( 1 <= i <= n)。如果這個元素比len長的序列的最大值大。則把這個元素直接新增到b陣列的後面。如果這個元素比b陣列的第乙個元素還要小則把這個元素賦給b陣列的第乙個值。否則進行二分查詢。當在b陣列裡面找到乙個數比a[i]小,並且他的後面的數大於或等於a[i]則跳出。將a[i]新增到這個數的後面。輸出len就可以了。

**如下:

#include

#include

int main()

if (b[mid] > a[i])

else

}b[j + 1] = a[i];}}

printf("%d\n", len);

}return 0;

求陣列中最長遞增子串行

原文見 分析過程很清楚。這裡主要是 部分有改動。完全用c寫的,從檔案中讀入。另外,解法二的程式加了去重,求的是最長單調遞增子串行。求陣列中最長遞增子串行 寫乙個時間複雜度盡可能低的程式,求乙個一維陣列 n個元素 中的最長遞增子串行的長度。例如 在序列1,1,2,3,4,5,6,7中,其最長的遞增子串...

求陣列中最長遞增子串行

最長遞增子串行,longest increasing subsequence 下面我們簡記為 lis。排序 lcs演算法 以及 dp演算法就忽略了,這兩個太容易理解了。假設存在乙個序列d 1.9 2 1 5 3 6 4 8 9 7,可以看出來它的lis長度為5。下面一步一步試著找出它。我們定義乙個序...

求陣列中最長遞增子串行

根據 程式設計之美 中解法二的思路,發現記錄lis陣列是不必要的,只要直接不斷更新maxv即可。在遍歷整個陣列arr的過程中,maxv陣列的長度也在不斷增加。當遍歷到arr i 時,maxv j 中已經記錄了由arr 0 arr i 的序列可以得到的所有長度為j的子串行中最大元素的最小值。例如 ar...