LIS:Longest Increasing Subsequence,最长递增子序列
话不多说,上题:
P1020 导弹拦截:
传送门
PS:这是一道求最长递增子序列的题,但要注意,本题开启spj,n方100分,nlogn200分。 (很坑很坑)
n方做法,代码如下:
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int dp1[100005];
int dp2[100005];
int main()
{
int n=0;
while(cin>>a[n++]);
n--;
fill(dp1,dp1+n,1);
fill(dp2,dp2+n,1);
for(int i=0; i<n; ++i)
{
for(int j=0; j<=i-1; ++j)
{
if(a[i]>a[j])
dp1[i]=max(dp1[i],dp1[j]+1);
else
dp2[i]=max(dp2[i],dp2[j]+1);
}
}
cout<<*max_element(dp2,dp2+n)<<endl;
cout<<*max_element(dp1,dp1+n)<<endl;
return 0;
}
可惜这种做法20个数据过了一半,后一半全部TLE了。
再上nlogn的做法,代码如下:
其核心思路就是二分。本人刚刚看到这种做法时表示不信,还模拟了一遍,结果就是打脸哦!!!
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int low[100005];
int bf(int r,int x)
{
int l=1,mid;
while(l<=r)
{
mid=l+r>>1;
if(x>low[mid])
r=mid-1;
else
l=mid+1;
}
return l;
}
int bs(int r,int x)
{
int l=1,mid;
while(l<=r)
{
mid=l+r>>1;
if(x<=low[mid])
r=mid-1;
else
l=mid+1;
}
return l;
}
int main()
{
int n=1;
while(cin>>a[n++]);
n-=2;
low[1]=a[1];
int ans=1;
for(int i=2; i<=n; ++i)
{
if(low[ans]>=a[i])
low[++ans]=a[i];
else
low[bf(ans,a[i])]=a[i];
}
cout<<ans<<endl;
low[1]=a[1];
ans=1;
for(int i=2; i<=n; ++i)
{
if(low[ans]<a[i])
low[++ans]=a[i];
else
low[bs(ans,a[i])]=a[i];
}
cout<<ans<<endl;
return 0;
}
二分查找写起来有点多,没关系,可以用函数来解决。STL大法好
先解释下所用的的函数:
lower_bound( )和upper_bound( )
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
lower_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
上代码:
#include <bits/stdc++.h>
using namespace std;
int a[100005];
int low[100005];
int main()
{
int n=1;
while(cin>>a[n++]);
n-=2;
low[1]=a[1];
int ans=1;
for(int i=2; i<=n; ++i)
{
if(low[ans]>=a[i])
low[++ans]=a[i];
else
low[upper_bound(low+1,low+ans+1,a[i],greater<int>())-low]=a[i];
}
cout<<ans<<endl;
low[1]=a[1];
ans=1;
for(int i=2; i<=n; ++i)
{
if(low[ans]<a[i])
low[++ans]=a[i];
else
low[lower_bound(low+1,low+ans+1,a[i])-low]=a[i];
}
cout<<ans<<endl;
return 0;
}
关于这两句语句也可以写成:
low[upper_bound(low+1,low+ans+1,a[i],greater<int>())-low]=a[i];
low[lower_bound(low+1,low+ans+1,a[i])-low]=a[i];
*upper_bound(low+1,low+ans+1,a[i],greater<int>())=a[i];
*lower_bound(low+1,low+ans+1,a[i])=a[i];
直接用指针返回,更加方便。
来源:CSDN
作者:Linux Unix
链接:https://blog.csdn.net/weixin_45714441/article/details/103746789