最長上公升子串行LIS

2021-10-25 05:58:52 字數 3378 閱讀 6434

三種方法:dp、貪心+二分、樹狀陣列維護

lis序列不一定唯一,但是長度是唯一的

f[i]代表以a[i]結尾的lis的長度

狀態轉移:$f[i]=max(f[j]+1,f[i]) \quad 1<=j#include

#include

#include

#include

#include

#include

using

namespace

std;

const

int maxn = 103, inf = 0x7f7f7f7f;

int a[maxn], f[maxn];

int n,ans = -inf;

intmain

()for(int i=1; i<=n; i++)

for(int j=1; j

if(a[j] < a[i])

f[i] = max(f[i], f[j]+1);

for(int i=1; i<=n; i++)

ans = max(ans, f[i]);

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

return0;}

新建乙個 low 陣列,low [ i ]表示長度為i的lis結尾元素的最小值。對於乙個上公升子串行,顯然其結尾元素越小,越有利於在後面接其他的元素,也就越可能變得更長。因此,我們只需要維護 low 陣列,對於每乙個a[ i ],如果a[ i ] > low [當前最長的lis長度],就把 a [ i ]接到當前最長的lis後面,即low [++當前最長的lis長度] = a [ i ]。

那麼,怎麼維護 low 陣列呢?

對於每乙個a [ i ],如果a [ i ]能接到 lis 後面,就接上去;否則,就用 a [ i ] 取更新 low 陣列。具體方法是,在low陣列中找到第乙個大於等於a [ i ]的元素low [ j ],用a [ i ]去更新 low [ j ]。如果從頭到尾掃一遍 low 陣列的話,時間複雜度仍是o(n^2)。我們注意到 low 陣列內部一定是單調不降的,所有我們可以二分 low 陣列,找出第乙個大於等於a[ i ]的元素。二分一次 low 陣列的時間複雜度的o(lgn),所以總的時間複雜度是o(nlogn)。

我們再舉乙個例子:有以下序列a[ ] = 3 1 2 6 4 5 10 7,求lis長度。

我們定義乙個b[ i ]來儲存可能的排序序列,len 為lis長度。我們依次把a[ i ]有序地放進b[ i ]裡.(為了方便,i的範圍就從1~n表示第i個數)

a[1] = 3,把3放進b[1],此時b[1] = 3,此時len = 1,最小末尾是3

a[2] = 1,因為1比3小,所以可以把b[1]中的3替換為1,此時b[1] = 1,此時len = 1,最小末尾是1

a[3] = 2,2大於1,就把2放進b[2] = 2,此時b[ ]=,len = 2

同理,a[4]=6,把6放進b[3] = 6,b[ ]=,len = 3

a[5]=4,4在2和6之間,比6小,可以把b[3]替換為4,b[ ] = ,len = 3

a[6] = 5,b[4] = 5,b[ ] = ,len = 4

a[7] = 10,b[5] = 10,b[ ] = ,len = 5

a[8] = 7,7在5和10之間,比10小,可以把b[5]替換為7,b[ ] = ,len = 5

最終我們得出lis長度為5。但是,但是!!這裡的1 2 4 5 7很明顯並不是正確的最長上公升子串行。是的,b序列並不表示最長上公升子串行,它只表示相應最長子序列長度的排好序的最小序列。這有什麼用呢?我們最後一步7替換10並沒有增加最長子序列的長度,而這一步的意義,在於記錄最小序列,代表了一種「最可能性」。假如後面還有兩個資料8和9,那麼b[6]將更新為8,b[7]將更新為9,len就變為7,可以自行體會它的作用。

因為在b中插入的資料是有序的,不需要移動,只需要替換,所以可以用二分查詢插入的位置,那麼插入n個數的時間複雜度為〇(logn),這樣我們會把這個求lis長度的演算法複雜度降為了〇(nlogn)。

lower_bound(startpos,endpos,value):在區間[startpos,endpos)[startpos,endpos) [startpos, endpos)[startpos,endpos)內找到第乙個大於等於valuevalue,如果找不到則返回endposendpos

upper_bound(startpos,endpos,value) [startpos, endpos)[startpos,endpos)內找到第乙個大於valuevalue,如果找不到則返回endposendpos

#include

#include

#include

using

namespace

std;

int num[10]=;

const

int inf=0x3f3f3f3f;

int l=10, g[100], d[100];

intmain

()

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

return

0;

} 這裡主要注意一下lower_bound函式的應用,注意減去的g是位址。

位址 - 位址 = 下標。

我們再來回顧o($n^$)dp的狀態轉移方程:$f[i]=max(f[j]+1,f[i]) \quad 1<=j還有一點需要注意:樹狀陣列求lis不去重的話就變成了最長不下降子串行了。

#include

#include

#include

#include

#include

#include

using

namespace

std;

const

int maxn =103,inf=0x7f7f7f7f;

struct

nodez[maxn];

int t[maxn];

int n;

bool

cmp(node a,node b)

void

modify

(int x,int y)

//把val[x]替換為val[x]和y中較大的數

intquery

(int x)

//返回val[1]~val[x]中的最大值

intmain

()sort(z+1,z+n+1,cmp);//以權值為第一關鍵字從小到大排序

for(int i=1;i<=n;i++)//按權值從小到大列舉

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

return0;}

最長上公升子串行 LIS

題目 兩道題幾乎一樣,只不過對於輸入輸出的要求有所不同罷了。lis有兩種方法 一 第一種方法 時間複雜度為o n 2 狀態 dp i 區間為0 i的序列的lis 轉移方程 dp i max 1,dp k 1 0 k include include include include using name...

最長上公升子串行LIS

問題 給定n個整數a1,a2,a3,a4,a5,an,從左到右的順序盡量選出多個整數,組成乙個上公升子串行,相鄰元素不相等。例如 1,6,2,3,7,5,它的最長上公升子串行為 1,2,3,5。分析 剛開始想這個問題的時候我想用遞迴來解決問題,可是後來考慮到遞迴的時間複雜度高,就覺得不能使用,並且本...

LIS 最長上公升子串行

最長遞增子串行問題 在一列數中尋找一些數,這些數滿足 任意兩個數a i 和a j 若i 設dp i 表示以i為結尾的最長遞增子串行的長度,則狀態轉移方程為 dp i max,1 j 這樣簡單的複雜度為o n 2 其實還有更好的方法。考慮兩個數a x 和a y x 按dp t k來分類,只需保留dp ...