【每日一题】补档 CF1875 D. Jellyfish and Mex | 动态规划 | 中等

文章讲解了如何通过动态规划解决一个涉及数组元素删除以减小mex值的问题,给出了解决方案并提供了C++代码示例。
摘要由CSDN通过智能技术生成

题目内容

原题链接

给定一个长度为 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 1n5000
  • 0 ≤ a i ≤ 1 0 9 0\leq a_i\leq 10^9 0ai109

题解

考虑 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×(cnti1)+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 cnti1 次对 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×(cnti1)+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;
}
  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值