-
简单方法
状态设计:F[i]代表以A[i]结尾的LIS的长度
状态转移:F[i]=max{F[j]+1}(1<=j< i,A[j]< A[i])
边界处理:F[i]=1(1<=i<=n)
时间复杂度:O(n^2)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 103,INF=0x7f7f7f7f;
int a[maxn],f[maxn];
int n,ans=-INF;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[i]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<i;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);
return 0;
}
-
贪心+二分
新建一个low数组,low[i]表示长度为i的LIS结尾元素的最小值。
对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。
因此,我们只需要维护low数组,对于每一个a[i],如果a[i] > low[当前最长的LIS长度],就把a[i]接到当前最长的LIS后面,即low[++当前最长的LIS长度]=a[i]。
对于每一个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)。
#include <iostream>
using namespace std;
#include <cstdio>
const int MaxN=100001;
int n,i,top=0,x,stack[MaxN];
int main(){
cin>>n;
stack[top]=-1;
for(i=1;i<=n;i++){
cin>>x;
if(x>stack[top]){stack[++top]=x;}
else
{
int low=0,high=top,mid;
while(low<high){
mid=(low+high)>>1;
if(x>stack[mid])
low=mid+1;
else
high=mid-1;
}
stack[low]=x;
}
}
cout<<top;
return 0;
}
UVA10635 Prince and Princess
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
using namespace std;
const int maxn=250*250+10;
const int INF=1e6+5;
int n,a,b;
int num[maxn],p[maxn],q[maxn];
int main()
{
int t,x,cnt=1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&a,&b);
memset(num,0,sizeof(num));
int ans=0;
for(int i=1;i<=a+1;i++)
{
scanf("%d",&x);
num[x]=i;
}
int l=0;
for(int i=0;i<=b;i++)
{
scanf("%d",&x);
if(num[x])
{
p[l]=num[x];
l++;
}
}
for(int i=1;i<=l;i++)
q[i]=INF;
for(int i=0;i<l;i++)
{
int k=lower_bound(q+1,q+l+1,p[i])-q;
q[k]=p[i];
printf("%d %d\n",k,q[k]);
ans=max(ans,k);
}
printf("Case %d: %d\n",cnt++,ans);
}
return 0;
}