問題描述(poj2533):對於給定的n個整數A1, A2…An, 從左到右的順序選擇儘可能多的數,組成一個上升子序列。(上升子序列可以理解爲:刪除0個或多個數, 其他數的順序不變, 例如1, 6, 2, 3, 7, 5, 可以選出上升子序列1, 2, 3, 5 也可以選出 1, 6, 7)
poj2533 : 求最長上升子序列的長度
法一:通過DP記憶化搜索求解問題,時間複雜度爲O(n^2)
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<string>
#include <algorithm>
using namespace std;
int n, m, i, j, mx;
const int maxn = 1010;
int a[maxn], d[maxn];
//記憶化搜索
//狀態:d[i]:第i個數的最長子序列的長度
//狀態轉移爲d[i] = max(d[i], d[j] + 1);
int main()
{
while(~scanf("%d",&n)){ //共有n個數
for(i = 0; i < n;i++) //錄入n個數保存在數組a[]中
scanf("%d",&a[i]);
for (i = 0; i < n; i++)
{
d[i] = 1; //初始化數組d[]爲1,
for(j = 0;j < i;j++)
if(a[j] < a[i])
d[i] = max(d[i], d[j] + 1); //第i個數的最長子序列長度 等於 比 第i個數小 的 數的最長子序列加1
}
mx = 0;
for(i = 0;i < n;i++)
mx = max (mx ,d[i]);
printf("%d\n",mx);
}
return 0;
}
法二:用DP二分搜索解決問題降低時間複雜度,該方法的時間複雜度爲O(nlogn)
實現:開一個棧,每次取棧頂元素top和讀到的元素temp做比較,如果temp > top 則將temp入棧;如果temp < top則二分查找棧中的比temp大的第1個數,並用temp替換它。 最長序列長度即爲棧的大小top。
舉例:原序列爲1,5,8,3,6,7
棧爲1,5,8,此時讀到3,用3替換5,得到1,3,8; 再讀6,用6替換8,得到1,3,6;再讀7,得到最終棧爲1,3,6,7。最長遞增子序列爲長度4。
如果想要輸出最長有序子數列的話,可以在法二的基礎上稍作改動。
用一個b【i】記錄第i個元素在隊列中的位置,然後逆向尋找,依次輸出。
//二分搜索
#include <iostream>
#define SIZE 1001
using namespace std;
int main()
{
int i, j, n, top, temp;
int stack[SIZE];
cin >> n;
top = 0;
stack[0] = -1;//令棧頂元素爲-1
for (i = 0; i < n; i++)
{
cin >> temp;
/* 比棧頂元素大數就入棧 */
if (temp > stack[top])
{
stack[++top] = temp;
}
else
{
int low = 1, high = top;
int mid;
/* 二分檢索棧中比temp大的第一個數 */
while (low <= high)
{
mid = (low + high) / 2;
if (temp > stack[mid])
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
/* 用temp替換 */
stack[low] = temp;
}
}
/* 最長序列數就是棧的大小 */
cout << top << endl;
//system("pause");
return 0;
}