题目大意:有一个n个数的数组a,数m初始为0,每次操作可以删除任意一个数,然后m加上那个数,求n次操作和m的最小值
1<=n<=5000;0<=a[i]<=1e9
思路:可以发现,如果我们要删除某个数,那么一定要把所有和这个数相等的数全部删去,这样才能使MEX变小,同时,所有大于MEX的数删去的花费都是0,所以我们每次操作的数的大小都是递减的,且只会操作MEX到0。
那么我们令dp[i]等于MEX等于i时的最小花费,我们从MEX到0枚举i,同时枚举该删哪个数,也就是从0到i-1遍历,当前最小花费就是不删这个数dp[j],或者删这个数也就是dp[i]+当前MEX*(这个数数量-1)再加这个数,转移方程为dp[j]=min(dp[j],dp[i]+i*(cnt[j]-1)+j)
cpp
//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e3 + 5;
ll n;
ll a[N];
ll cost[N];
ll cnt[N];
void init()
{
for (int i = 0; i <= n; i++)
{
cost[i] = 1e18;
cnt[i] = 0;
}
}
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
ll lowbit(ll x)
{
return x & (-x);
}
void solve()
{
ll m;
cin >> n;
init();
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] <= n)
{//MEX最大为n,大于n的都可以随便删
cnt[a[i]]++;
}
}
int mex = 0;
while (cnt[mex])
{//找当前的MEX
mex++;
}
cost[mex] = 0;
for (ll i = mex; i >= 0; i--)
{
for (ll j = 0; j < i; j++)
{
cost[j] = min(cost[j], cost[i] + i * (cnt[j] - 1) + j);
}
}
cout << cost[0] << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
solve();
}
return 0;
}