Codeforces Round #762 (Div. 3) E. MEX and Increments

前言

  • 寒假开始搞一搞

题目

  • 原题地址:E. MEX and Increments
  • 题目编号:1619E
  • 目标算法:dp(动态规划)
  • 难度评分:1700

1.题目大意

  • 给你一个由n个非负数构成的数组
  • 定义MEX为数组中不存在的最小非负数,例如:
    • [3, 1, 0]      MEX = 2
    • [3, 3, 1, 4]  MEX = 0
  • 要求使MEX = 0到n需要对数组进行最小的操作次数,无法实现输出-1
  • (一次操作就是对数组中任意元素+1)

2.题目分析

  • 首先有几个注意要点
    1. 当前i对应的MEX不满足题意为-1时,后面大于i的数对应的MEX均为-1
    2. 当前i对应的MEX满足题意时,前面必然可以构造出一个小序列,满足0到i的元素有且只有一个。
    3. 构造小序列的最新一位i-1时,要从已构造元素的重复元素中选出最接近i-1的一个进行构造。
  • 根据要点2以及题目要求,我们可以得到以下公式:
  • ans = dp + cnt
  • 其中:
    • ans:要求的最小操作数
    • dp:构造小序列所需的最小操作数
    • cnt:当前数值元素的个数
  • 根据要点3,进一步添加2个优先队列:
    • up:升序,用于存储样例中的所有元素
    • down:降序,用于存储小于当前i的重复元素
  • 以样例中的0 1 2 3 4 3 2为例进行演示
MEX up down dp cnt ans
0 0 1 2 2 3 3 4 0 1 0 + 1 = 1
1 1 2 2 3 3 4 0 1 - 1 - 0 + 0 = 0 1 0 + 1 = 1
2 2 2 3 3 4 1 2 - 1 - 1 + 0 = 0 1 0 + 1 = 1
3 3 3 4 2 2 3 - 1 - 2 + 0 = 0 2 0 + 2 = 2
4 4 3 3 2 4 - 1 - 3 + 0 = 0 2 0 + 2 = 2
5 4 3 2 5 - 1 - 4 + 0 = 0 0 0 + 0 = 0
6 3 2 6 - 1 - 3 + 0 = 2 0 2 + 0 = 2
7 2 7 - 1 - 2 + 2 = 6 0 6 + 0 = 6
  • 得到最终答案1 2 2 1 0 2 6

3.题目代码

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        int tmp;
        cin >>n;
        long long cnt[200005]={0};//有可能有大于n的数,所以数组要开到最大
        priority_queue<long long, vector<long long>,greater<long long>> up;//升序
        priority_queue<long long, vector<long long>,less<long long>> down;//降序
        for(int i=0;i<n;i++)//统计个数
        {
            cin >> tmp;
            cnt[tmp]++;
            up.push(tmp);
        }
        long long dp = 0;//创造小序列的操作数
        long long ans = 0;
        cout << cnt[0] << " ";
        for(int i=1;i<=n;i++)
        {
            while(!up.empty()&&up.top()<i)
            {
                down.push(up.top());
                up.pop();
                if(up.empty())
                    break;
            }
            if(down.empty())//构造不出小序列
            {
                for(int j=i;j<=n;j++)
                    cout << -1 << " ";
                break;
            }
            else
            {
                dp += ((i-1) - down.top());//构造小序列
                down.pop();
                ans = dp + cnt[i];
                cout << ans << " ";
            }
        }
        cout << endl;
    }
    return 0;
}
  • ps:在做题的过程中发现对于0的特判可以直接包含进去,于是以上代码就是最后的版本
posted @   仪战群儒  阅读(152)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示