F2. Flying Sort 最长上升连续子序列

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);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值