赛后第三天才开始补提,太怠惰了qaq,比赛那天不知道咋的困得要死,一天睡了12小时,比赛也没怎么比,几乎睡过去了
题意:
一个数字序列,进行一下两种操作:
①Invert:将第一数字移动到最后一个位置
②Drop-2:将倒数第二个数字移动到第一个位置
连续进行Drop-2操作,记作一个multi-drop,问将数字序列恢复成1,2,。。。n需要多少次mutil-drop操作
对于Invert操作,它不会改变操作前数字序列的相对位置,只是将数字的绝对位置给改变了
对于Drop-2操作,它能够改变数字序列的相对位置和绝对位置,绝对位置的改变对于恢复成目标序列的意义不大,不用考虑,主要考虑的是其使得相对位置改变的作用,进行Invert操作可以将数字序列的绝对位置改变,在进行Drop-2操作,可以达到将一数字移动到任意位置的效果
例:
1 2 5 3 4
Invert: 3 4 1 2 5
Drop-2: 1 2 3 4 5
1 5 2 3 4
Invert: 2 3 4 1 5
Drop-2: 1 2 3 4 5
1 5 3 2 4
Invert: 5 2 4 1 5
Drop-2: 1 3 2 4 5
Invert: 4 5 1 3 2
Drop-2: 3 4 5 1 2
Invert: 1 2 3 4 5
所以我们需要找到不符合条件的数字将其移动到它应该在的位置,所谓的不符合条件即不符合1,2,…n这样规律的数,也就是不符合递增序列的数,所以我们只需要找到一条最长的递增子序列,不在这个序列中的数都是需要进行移动的数,所以答案为序列长度-最长递增子序列,注意由于Invert操作的存在,我们的序列开始位置是不确定的,即我们需要从所有数位置作为开始寻找最长子序列
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 2020;
int n;
int up[N], arr[N];
int find(int k) {
up[1] = arr[k];
int len = 1;
for (int i = k+1; i <=k+n-1 ; i++) {
if (arr[i] > up[len]) up[++len] = arr[i];
else {
int l = 1, r = len;
while (l <= r) {
int mid = (l + r) / 2;
if (up[mid] >= arr[i]) {
r = mid - 1;
}
else {
l=mid+1;
}
}
up[l] = min(up[l], arr[i]);
}
}
return len;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
arr[i + n] = arr[i];
}
int len = 0;
for (int i = 1; i <= n; i++) {
len = max(len, find(i));
}
printf("%d\n", n - len);
}
补题参考题解:https://blog.nowcoder.net/n/1e4de66e422a47fdb04c246cdb2e30ed