牛客小白月赛20G 快乐风男
算法分析
这道题就是求最长上升子序列,就是多了个寻找下标编号并要求下标字典序最小,是下标不是子序列;
算法实现
#include<string>
#include<iostream>
#include<math.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int a[maxn],len[maxn],dp[maxn],res[maxn];
//a代表原始数组,len为当前最长子序列的个数,
//dp只是用来辅助记录大小的,不是真正的所选序列
//res为最终答案下标的最小序列
int cnt;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(len,0,sizeof(len));
cnt=1;
dp[1]=a[1],len[1]=1;
for(int i=2;i<=n;i++)
{
if(a[i]>dp[cnt])
{
cnt++;
len[i]=cnt;//长度增加,记录当前字符所拥有的最长子序列个数
dp[cnt]=a[i];
}
else
{
int pos=lower_bound(dp+1,dp+1+cnt,a[i])-dp;//找到dp数组中第一个大于或等于a[i]的下标
dp[pos]=a[i];
len[i]=pos;//所在位置下标就是当前字符能拥有的最长子序列的个数
}
}
memset(res,0,sizeof(res));
for(int i=n;i>=1;i--)//从后往前保证下标和最小
{
if(len[i]==cnt||a[i]<a[res[len[i]+1]])
res[len[i]]=i;
//a[i]<a[res[len[i]+1]]是保证当前字符大小小于后面一位
}
cout<<cnt<<endl;
for(int i=1;i<cnt;i++)
cout<<res[i]<<" ";
cout<<res[cnt]<<endl;
}