F2. Flying Sort 最长上升连续子序列
题目链接https://codeforces.com/contest/1367/problem/F2
题意:给一个数组,每次可以将任意位置的数放到最前或最后,最后是数组非降序,输出最小操作次数。
不难想到答案就是(n-最长连续上升子序列的长度)
先说明下定义
如序列 4 2 6 3 5 1
它的最长上升子序列是 2 3 5
在这个基础上还要连续,将序列排序为1 2 3 4 5 6
满足要求的最长连续上升子序列是 4 5或2 3
如果数组不存在相等的数,那很容易,直接将数组离散化后
遍历a数组,dp[a[i]]=dp[a[i]-1]+1,既可找到最长长度;
dp[a[i]]的意思是,在第i个位置时,选取a[i]作为序列的末尾,满足要求的序列能有多长
但是存在相同的数的话这样写就会有bug
如 1 2 3 2 4
最后的结果会是4,序列为1 2 3 4
但是在1 3 中间只有一个2结果并不连续,关键就是解决这个1 3的连接问题怎么把1的答案贡献给3
我想到的办法是更改离散化的方法
如,将1 2 2 2 3 10,变成 1 2 2 2 5 6
很容易发现这样我空出了dp[3]与dp[4]
dp[3]就用来存积累2次为2的答案,dp[4]就用来存积累3次为2的答案
dp[5]的值就可以从dp[4]更新了,摆脱了之前dp[2]中无法判断是否可以取2之前的1,使结果变成1 2 3 4的错误
每次遍历a[i]=2的时候,a[i]既可以作为第一次的2,也可以作为第2次与第3次的
从第3次开始dp[a[i]+2]=dp[a[i]+1]+1;
到第1次dp[a[i]]=dp[a[i]-1]+1;
需要操作多少次用一个sum[a[i]]存a[i]相同的个数既可。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct madoka{
int p;
int z;
}lin;
int t,n,dp[200007],sum[200007],ans,a[200007];
vector<madoka>ho;
bool cmp(madoka a1,madoka a2){
return a1.z<a2.z;
}
int main(){
scanf("%d",&t);
while(t--){
ans=0;
ho.clear();
scanf("%d",&n);
for(int i=1;i<=n;i++){
dp[i]=0;
sum[i]=0;
scanf("%d",&a[i]);
lin.p=i;
lin.z=a[i];
ho.push_back(lin);
}
sort(ho.begin(),ho.end(),cmp);
int ok=1;
for(int i=0;i<n;i++){
if(i!=0&&ho[i].z==ho[i-1].z){
a[ho[i].p]=a[ho[i-1].p];
}
else{
a[ho[i].p]=ok;
}
ok++;
}
for(int i=1;i<=n;i++){
sum[a[i]]++;
}
for(int i=1;i<=n;i++){
for(int j=a[i]+sum[a[i]]-1;j>=a[i];j--){
dp[j]=dp[j-1]+1;
}
}
for(int i=1;i<=n;i++){
ans=max(ans,dp[i]);
}
printf("%d\n",n-ans);
}
}