题目内容
给定一个长度为 n n n 的数组 a a a ,每次选择一个元素 a i a_i ai 删除,删除的代价为删除后剩余元素的 m e x mex mex , m e x ( a ) mex(a) mex(a) 是指 a a a 中未出现过的最小的非负数。
问将数组 a a a 删除为空的操作的最小代价。
数据范围
- 1 ≤ n ≤ 5000 1\leq n\leq 5000 1≤n≤5000
- 0 ≤ a i ≤ 1 0 9 0\leq a_i\leq 10^9 0≤ai≤109
题解
考虑 m e x ( a ) mex(a) mex(a) ,对于 a a a 中大于 m e x ( a ) mex(a) mex(a) 的数,必然放到最后使得 m e x ( a ) = 0 mex(a)=0 mex(a)=0 时再操作,其操作代价为 0 0 0 。
令数组 a a a 的 m e x ( a ) mex(a) mex(a) 为 m m m, c n t i cnt_i cnti 表示元素 i i i 在 a a a 中的出现次数。
那么我们只需要考虑所有小于等于 m e x ( a ) mex(a) mex(a) 的数如何删除。
状态定义
f i f_i fi 表示将 m e x ( a ) mex(a) mex(a) 变成 i i i 的最小操作代价。
状态转移
f i = min j = i + 1 m ( f j + j × ( c n t i − 1 ) + i ) f_i=\min\limits_{j=i+1}^m (f_j+j\times (cnt_i-1)+i) fi=j=i+1minm(fj+j×(cnti−1)+i)
即我们考虑将 m e x ( a ) mex(a) mex(a) 变成 i i i 时,其之前的 m e x ( a ) mex(a) mex(a) 必然大于 i i i ,那么遍历 j ∈ [ i + 1 , m ] j\in [i+1,m] j∈[i+1,m] 作为 m e x ( a ) mex(a) mex(a) 。
对于 m e x ( a ) mex(a) mex(a) 从 j j j 降低到 i i i 的情况,就是直接将所有的 i i i 都删除。
对于前 c n t i − 1 cnt_i-1 cnti−1 次对 i i i 的删除操作,其代价为 j j j ,对于第 c n t i cnt_i cnti 次对 i i i 的删除操作,删除后的 m e x ( a ) mex(a) mex(a) 已然变成了 i i i ,其代价为 i i i 。
故总代价为 f j + j × ( c n t i − 1 ) + i f_j+j\times (cnt_i-1)+i fj+j×(cnti−1)+i ,取所有 j j j 的最小值即可。
最终答案为: f 0 f_0 f0
时间复杂度: O ( n 2 ) O(n^2) O(n2)
代码
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, x;
cin >> n;
vector<int> cnt(n);
for (int i = 0; i < n; ++i) {
cin >> x;
if (x < n) cnt[x] += 1;
}
int mex = 0;
while (mex < n && cnt[mex] > 0) mex += 1;
// f[i] 表示使得 mex 变成 i 的最小操作次数
const long long INF = 1e18;
vector<long long> f(mex + 1, INF);
f[mex] = 0;
for (int i = mex - 1; i >= 0; --i)
for (int j = i + 1; j <= mex; ++j)
f[i] = min(f[i], f[j] + 1ll * (cnt[i] - 1) * j + i);
cout << f[0] << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}