问题描述(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;
}