F2. Flying Sort (Hard Version)
F2. Flying Sort (Hard Version)
题目大意:
给你一个大小是n的序列,有两种操作可以进行:
- 选一个数字放在最前面
- 选一个数字放在最后面
这个序列可能含有相同的数字,问最少的操作让这个序列变成一个不递减的序列。
题解:
其实这个题目难度不大,思考一会就知道要怎么求,但是这个dp的形式我很少遇到,所以记录一下。
容易得到,最多是n次一定可以做到,那么接下来就是考虑选择一个最长子序列,使得保持这个子序列不变,其他元素经过操作之后变成一个不递减的序列。
这种题目,首先要做的就是研究这个操作的实质,容易发现,如果选择的这个子序列中间需要插入值,那么肯定是不符合要求的,所以要找的这个子序列就是一个大小关系是连续的。
但是由于这个可能有重复的数字,所以让这个dp的难度变大了,接下来就是具体的dp定义和dp转移,在代码里面。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int last[maxn],cnt[maxn],fir[maxn],num[maxn];
int a[maxn],dp[maxn],v[maxn];
/**
dp[i] 表示到位置i的最长的序列
last[i] 表示上一个大小是i的位置
fir[i] 表示第一个i出现的位置
num[i] 表示i的数量
cnt[i] 表示i到目前为止出现的次数
转移肯定只能向比自己小1的转或者和自己相等的在前面的转移:
第一种转移:如果x不是第一个,那么可以往前面的转移,dp[i]=dp[last[x]]+1
第二种转移:如果x-1全部都出现了,那么往x-1的第一个转移  dp[i] = dp[fir[x-1]]+cnt[x-1];
第三种转移:如果x-1不是全部都出现了,那么x-1必须当第一个,所以 dp[i] = cnt[x-1]+1
**/
void init(int n){
	for(int i=0;i<=n+10;i++){
		dp[i] = last[i] = fir[i] = num[i] = cnt[i] = 0;
	}
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n;
		scanf("%d",&n);
		init(n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			v[i] = a[i];
		}
		sort(v+1,v+1+n);
		int len = unique(v+1,v+1+n)-v-1;
		for(int i=1;i<=n;i++) {
			a[i] = lower_bound(v+1,v+1+len,a[i])-v;
			num[a[i]]++;
		}
		// printf("sss\n");
		int ans = 0;
		for(int i=1;i<=n;i++){
			int x = a[i];
			dp[i] = max(dp[i],cnt[x-1]+1);
			if(last[x]) dp[i] = max(dp[last[x]]+1,dp[i]);
			if(cnt[x-1]==num[x-1]) dp[i] = max(dp[i],dp[fir[x-1]]+cnt[x-1]);
			cnt[x]++,last[x] = i;
			if(!fir[x]) fir[x] = i;
			ans = max(ans,dp[i]);
		}
		printf("%d\n", n-ans);
	}
}
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号