dp:o(n^2)
设dp[i]为以i结尾的最长非降子序列,则dp[i]=max(dp[i],dp[j]+1),其中a[i]>=a[j]
贪心+二分o(nlogn)
用一个新的数组f记录选出的非降子序列,若a[i]>=f[top],则将a[i]插入f,更新top,否则由贪心策略,考虑用a[i]来替换f中第一个>=a[i]的数,使得f更具作为最长非降子序列的潜力。(注意这样得到的不一定真为非降的,因为替换的值可能为处于中间的值,并不满足元顺序,但长度等价)
e.g 4 0 5 8 10 1 8
4->0 -> 0 5 -> 0 5 8->0 5 8 10->0 1 8 10
#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
int a[50050];
int f[50050];
int main()
{
int n;
cin>>n;
far(i,0,n)
scanf("%d",&a[i]);
int l=1;
f[0]=a[0];
far(i,1,n)
{
if(a[i]>f[l-1])
{
f[l]=a[i];
++l;
}
else
{
int p=lower_bound(f,f+l,a[i])-f;
f[p]=a[i];
}
}
printf("%d\n",l);
}
获取LIS关键元素
即长度一样的LIS中公共元素
hdu6635:Nonsense Time
theme:给定长度为n的序列,开始时所有元素都被冻住,即不可用,然后没次解冻某个元素Api,问当前可用元素的最长非降子序列长度为多少 ?1<=n<=50000,1<=Ai<=n,1<=pi<=n
solution:逆向考虑。先求出所有元素的最长非降子序列,然后倒序删除元素,若被删除的元素不是上一个最长非降子序列的关键元素,则长度不变,冻结它就行,否则冻住它,再算一遍最长非降子序列。用贪心+二分算最长非降子序列复杂度为o(nlogn),由于数据随机,所以LIS期望长度为sqrt(n),所以总时间复杂度为o(sqrt(n)*n*logn).
#include<bits/stdc++.h>
typedef long long LL;
#define inf 0x3f3f3f3f
using namespace std;
#define far(i,s,n) for(int i=s;i<n;++i)
int a[50050],fidx[50050];
int dp[50050],idx[50050],belong[50050];//dp存LIS元素,idx[i]表示a[i]能在dp中的位置下标,belong[i]标识a[i]是否为LIS关键元素
int ans[50050];
int n;
void Find()//找LIS
{
fill(dp,dp+n,inf);
fill(idx,idx+n,-1);
fill(belong,belong+n,0);
int maxl=0;
far(i,0,n)
{
if(a[i]!=inf)//表示a[i]没被冻住
{
int pos=lower_bound(dp,dp+n,a[i])-dp;
if(dp[pos]==inf)//a[i]比dp里所有元素都大,则把它加在后面
{
dp[pos]=a[i];
idx[i]=pos;
maxl = pos;
}
else
{
dp[pos]=a[i];//用较小的值替换
idx[i]=pos;
}
}
}
for(int i=n-1;i>=0;--i)//判断每个元素是否为关键元素,即每个该长度的LIS都有它
{
if(maxl<0)
break;
if(idx[i]==maxl)
{
belong[i]=1;
--maxl;
}
}
}
int main()
{
int Case;
cin>>Case;
while(Case--)
{
scanf("%d",&n);
far(i,0,n)
scanf("%d",&a[i]);
Find();
far(i,0,n)
{
scanf("%d",&fidx[i]);
--fidx[i];
}
for(int i=n-1;i>=0;--i)
{
ans[i]=lower_bound(dp,dp+n,inf)-dp;
if(belong[fidx[i]]==1)
{
a[fidx[i]]=inf;
Find();
}
else
a[fidx[i]]=inf;
}
far(i,0,n)
{
if(i==n-1)
printf("%d\n",ans[i]);
else
printf("%d ",ans[i]);
}
}
return 0;
}
用树状数组
对原数组排序后,依次插入,每个前缀和表示以该节点结尾的最长非降子序列的值。
#include<bits/stdc++.h>
#define far(i,t,n) for(int i=t;i<n;++i)
#define endl "\n"
#define spa " "
#define pk(x) push_back(x)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ll tree[200020];
int lowbit(int x)
{
return x&(-x);
}
void update(int x,ll var)
{
while(x<200020)
{
tree[x]=max(tree[x],var);
x+=lowbit(x);
}
}
ll query(int x)
{
ll ans=0;
while(x)
{
ans=max(ans,tree[x]);
x-=lowbit(x);
}
return ans;
}
ll a[200020];
vector<ll>v;
int main()
{
int n;
cin>>n;
far(i,0,n)
{
scanf("%lld",&a[i]);
v.pk(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
ll ans=0;
far(i,0,n)
{
ll h;
scanf("%lld",&h);
int x=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;//树状数组下标从1开始
ll tmp=query(x);
ans=max(ans,tmp+h);
// cout<<x<<" "<<tmp<<" "<<ans<<endl;
update(x,tmp+h);
}
cout<<ans<<endl;
}