最长上升子序列的dp球阀还是比较简单的,转移方程爷比较好写,f[i]=max(f[i],f[j]+1)==>(f[j]>=f[i]&&a[j]<a[i])
现在说一下二分的方法:主要的思路就是保存一个栈,每当循环一个数的时候,与栈顶元素进行比较,如果大于栈顶元素就直接入栈,否则就在栈中二分查找,直到吵到比他大的第一个元素,用当前的元素来代替这个元素,最后这个栈的容量就是所要求的长度
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mid (l+r)/2
#define F(i,x,y) for(i=x;i<=y;++i)
int n,top=0,l,r,stack[1001];
int main()
{
freopen("lis1.in","r",stdin);
freopen("lis1.out","w",stdout);
int i,j,t;
scanf("%d",&n);
stack[0]=-1;
F(i,1,n)
{
scanf("%d",&t);
if(t>stack[top])
{
top+=1;
stack[top]=t;
}
else
{
l=1;r=top;
while(l<=r)
{
if(t>stack[mid]) l=mid+1;
else r=mid-1;
}
stack[l]=t;
}
}
printf("%d\n",top);
}
其实二分的求发在有的时候海参比较好用的
放一道题:题目大概就是给定一个环,求出最长上升子序列。n<=50000
有一个类似于偏分的方法就是每次求出一个LIS之后没我们就从这个LIS开始的地方把这之前的数列全部放到最后面去,因为在前面的那一段中不会再出现更优的解了,这样由于随机的数据,程序跑起来还是比较快的。但在这个时候就需要注意要用二分的方法来求这个LIS,同时由于我们还需要求出起点在哪,所以要倒着求出最长下降子序列,求出的终点即为LIS的起点。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define F(i,x,y) for(i=x;i<=y;++i)
#define Fr(i,x,y) for(i=x;i>=y;--i)
#define N n*2-1
#define mid (l+r+1)/2
int n,a[100010]={0},f[100010]={0},ans=0,now=1;
int stack[50001]={0},top=0;
void work(int x,int y)
{
int i,j,l=0,r=0,last=now,top=0;
Fr(i,x,y)
{
l=0;r=top;
while(l!=r)
{
if(stack[mid]>a[i]) l=mid;
else r=mid-1;
}
stack[l+1]=a[i];
if(l==top)
{
top+=1;
now=i;
}
}
ans=max(ans,top);
if(now==last) now+=1;
/*cout<<top<<' ';*/
}
int main()
{
freopen("LICS.in","r",stdin);
freopen("LICS.out","w",stdout);
int i,j;
scanf("%d",&n);
F(i,1,n)
{
scanf("%d",&a[i]);
a[n+i]=a[i];
}
do
{
work(now+n-1,now);
/*cout<<now<<endl;*/
}while(now<=n);
printf("%d\n",ans);
}