最長遞增子串行(LIS)

2021-06-27 13:39:06 字數 2790 閱讀 7654

300. longest increasing subsequence(good!)

給定乙個長度為n的陣列,找出乙個最長的單調遞增子串行(不一定連續,當時先後順序不能亂)。 更正式的定義是:

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

比如陣列a 為, 那麼最長遞增子串行為。

以i結尾的序列的最長遞增子串行和其[0, i - 1]「字首」的最長遞增子串行有關,設lis[i]儲存以i結尾的最長遞增子串行的長度

若i = 0,則lis[i] = 1;

若i > 0,則lis[i]的值和其[0, i - 1]字首的最長遞增子串行長度有關,用j遍歷[0, i - 1]得到其最長遞增子串行為lis[j],對每乙個lis[j],如果序列array[j]  < array[i]並且lis[j] + 1 > lis[i],則lis[i]的值變成lis[j] + 1。即:

lis[i] = max,其中array[i] > array[j] 且 j = [0, i - 1]。

最開始寫的程式貌似是對的

#includeusing namespace std;

//lis-longest increasing subsequent

int lis(int arry,int len)

} if(lis[i]>max)

max=lis[i];

} //可以再上個迴圈中操作

/* int max=-1;

for(int i=0;imax)

max=lis[i];

}*/delete lis;

return max;

}int main();

int len=9;

cout<

可以輸出相應的最長遞增項

#include#includeusing namespace std;

//lis-longest increasing subsequent

int lis(int arry,int len)

} tmp[i].push_back(arry[i]);

if(lis[i]>max)

max=lis[i];

} for(int i=0;i

再看乙個擴充套件問題

上面的時間複雜度是o(n^2),其實還有o(nlgn)的方法

《程式設計之美》對於這個方法有提到,不過它的講解我看得比較難受,好長時間才明白,涉及到的陣列也比較多,除了源資料陣列,有lis[i]和maxv[lis[i]],後來看了大牛felix的講解,我才忽然發現程式設計之美中的這個陣列maxv[lis[i]]在記錄資訊上其實是饒了彎的,因為我們在尋找某一長度子串行所對應的最大元素最小值時,完全沒必要通過lis[i]去定位,即沒必要與資料arr[i]掛鉤,直接將maxv[i]的下標作為lis的長度,來記錄最小值就可以了(表達能力太次,囧。。。),一句話,就是不需要lis[i]這個陣列了,只用maxv[i]即可達到效果,而且原理容易理解,**表達也比較直觀、簡單。

下面說說原理:

目的:我們期望在前i個元素中的所有長度為len的遞增子串行中找到這樣乙個序列,它的最大元素比arr[i+1]小,而且長度要盡量的長,如此,我們只需記錄len長度的遞增子串行中最大元素的最小值就能使得將來的遞增子串行盡量地長。

方法:維護乙個陣列maxv[i],記錄長度為i的遞增子串行中最大元素的最小值,並對於陣列中的每個元素考察其是哪個子串行的最大元素,二分更新

maxv

陣列,最終i的值便是最長遞增子串行的長度。這個方法真是太巧妙了,妙不可言。

#include #includeusing namespace std;

/* 最長遞增子串行 lis

* 設陣列長度不超過 30

* dp + binarysearch

*/int maxv[30]; /* 儲存長度i+1(len)的子串行最大元素的最小值 */

int len; /* 儲存子串行的最大長度 即maxv當前的下標*/

/* 返回maxv[i]中剛剛大於x的那個元素的下標 */

int binsearch(int * maxv, int size, int x)

else

}return left;

}int lis(int * arr, int size)

else

}return len;

}int main()

; /* 計算lis長度 */

printf("%d\n",lis(arr,sizeof(arr)/sizeof(int)));

return 0;

}

雙端lis問題,用動態規劃的思想可以解決,目標規劃函式為max,其中b[i]是從左到右的,0~i個數之間滿足遞增的數字個數;c[i]為從右到左的,n- 1 ~ i個數之間滿足遞增的數字個數。最後結果為n - max + 1(+1是因為b[i]和c[i]算重乙個)

#include#includeusing namespace std;

//返回最少刪除的數個數

int doublelis(int arry,int len)

} }for(int i=len-1;i>=0;i--)

} }for(int i=0;imax)

max=b[i]+c[i];

} delete b;

delete c;

return len-max+1;

}int main();

int len=8;

cout<

最長遞增子串行 LIS

對於這個問題,最直觀的dp方法是cnt i 表示以height i 結束的最長遞增子串行的元素的個數,遞迴方程是cnt i max for max i 0 i求出整個數列的最長遞增子串行的長度 if b i max max b i cout return 0 顯然,這種方法的時間複雜度仍為o n 2...

最長遞增子串行 LIS

給定乙個長度為n的陣列,找出乙個最長的單調自增子序列 不一定連續,但是順序不能亂 例如 給定乙個長度為6的陣列a,則其最長的單調遞增子串行為,長度為4.這個問題可以轉換為最長公共子串行問題。如例子中的陣列a,則我們排序該陣列得到陣列a 然後找出陣列a和a 的最長公共子串行即可。顯然這裡最長公共子串行...

LIS 最長遞增子串行

最長遞增子串行可以用動態規劃的方法,時間複雜度是o n n 也可以用二分查詢的方法,時間複雜度是o nlogn 首先看二分查詢的方法 思路是,順序插入資料,當插入的資料大於以往的任何值時,插入到最後,若插入的資料不是大於以往的任何值時,首先找到大於要插入資料的乙個值,然後,將要插入的資料將該值替換掉...