三種方法: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 ...