本题是求最长上升子序列(LIS)问题。
O(n^2)的思路:
令a[i]表示第i个元素,d[i]表示从a[1]到a[i]中以a[i]结尾的最长子序列长度。对于任意的0 < j <= i-1,如果a[i]>a[j],则a[i]可以接在a[j]后面形成一个以a[i]结尾的新的最长上升子序列。
DP状态转移方程: d[i] = max{d[i], d[j] + 1} (j = 1, 2, 3, ...,i-1 且a[j] < a[i])
解释一下这个方程,在1<=i<=n,1<=j<=i-1范围内:
默认初始化d[i] = 1,如果 a[j] <a[i] ,则d[i] = d[j] + 1
最后我们需要找出d[1~n]的最大值,即为所求。
AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int max(int x,int y)
{
return x>y?x:y;
}
int main()
{
int n,i,j;
int a[1010],d[1010];
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
d[i]=0;
}
int maxn=0;
for(i=1;i<=n;i++)
{
d[i]=1;//初始化
for(j=1;j<i;j++)
if(a[j]<a[i])
d[i]=max(d[i],d[j]+1);// d[i] = max{d[i], d[j] + 1}
if(maxn<d[i])maxn=d[i];//求d[i]的最大值
}
printf("%d\n",maxn);
}
return 0;
}
O(nlogn)的解法:
时间复杂度降低其实是因为这个算法里面用到了二分搜索。具体操作是建立一个栈stack[],每次取栈顶元素stack[j]和读到的元素a[i]做比较,如果a[i]>stack[j] 则将a[i]入栈;否则二分查找栈中的比a[i]大的第1个数,并用a[i]替换它。最长序列长度即为栈的长度。主要注意替代时是stack[low]=a[i];
AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;//poj2533
const int N=1010;
int main()
{
int n,i,j;
int a[N],stack[N];
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
j=0;
stack[0]=-1;
for(i=1;i<=n;i++)
{
if(a[i]>stack[j])
stack[++j]=a[i];
else
{
int low=1,top=j,mid;
while(low<=top)//二分
{
mid=(low+top)/2;
if(a[i]>stack[mid])
low=mid+1;
else
top=mid-1;
}
stack[low]=a[i];//如:在2,4,8的栈插入1或3,自己试一试
}
}
printf("%d\n",j);
}
return 0;
}