2023.6.16 每日一题
原题链接
B - Technocup 2020 - Elimination Round 1 - D
B. Sequence Sorting - 2000
题目大意
给定一个数组,定义一个操作:选定一个数,将所有值等于这个数的数移动到数组的一端(数组头或者数组尾)。问将数组变成非递减序列最少需要多少操作次数。
解题思路
对于每一种数,我们记录他们第一次出现的位置和最后一次出现的位置来界定他的跨度。
我们使用一个dp数组去找对于每一种元素,他最后一次出现的位置比下一个的第一次出现还要小的情况是否得到满足。
一旦存在满足如上条件的序列,那么这段序列涉及到的元素就不需要移动,求出这样的序列长度的最大值拿总数减去即可。
AC Code
#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
#define ios ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
const int MOD = 1e9 + 7;
LL a[N], fir[N], lst[N];
LL dp[N];
pair<int, pair<int, int>> p[N];
void solve() {
int n, cnt = 0;
cin >> n;
fill(fir + 1, fir + n + 1, 0);
fill(lst + 1, lst + n + 1, 0);
fill(dp, dp + n + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
if (!fir[a[i]]) {
cnt++;
fir[a[i]] = i;
}
lst[a[i]] = i;
}
for (int i = 1, j = 0; i <= n; ++i) {
if (fir[a[i]] == i) {
p[++j] = {a[i], {fir[a[i]], lst[a[i]]}};
}
}
sort(p + 1, p + cnt + 1);
dp[1] = 1;
for (int i = 2; i <= cnt; ++i) {
dp[i] = (p[i - 1].second.second < p[i].second.first) ? dp[i - 1] + 1 : 1;
}
cout << cnt - *max_element(dp + 1, dp + cnt + 1) << endl;
}
signed main() {
ios;
int T = 1;
cin >> T;
while (T--) {
solve();
}
}