[模拟] aw3664. 数组补全(模拟+贪心+中位数+思维)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[模拟] aw3664. 数组补全(模拟+贪心+中位数+思维)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:3664. 数组补全

2. 题目解析

有点考思维,模拟题。

思路:

  • 合法填充后要求数组中位数为 y,那就意味着小于 y 的数的个数有 n 2 \\frac n 2 2n 个,大于等于 y 个数的个数有 n 2 + 1 \\frac {n} 2 + 1 2n+1 个。
  • 若使总和最小,则填充进来的数应该尽可能小。
  • 输入时统计小于 y 的个数和大于等于 y 的个数。
    • 若小于 y 的个数已经大于了 n/2 则说明中位数一定不满足要求,则输出 -1。否则应该将小于 y 的个数填充至 n 2 \\frac n 2 2n 个,填充数应该选最小,选 1。
    • 若大于 y 的个数已经大于 n/2+1,是合法情况…不要进行误判。 若小于 n/2+1 的话,应该填充差值个 y,保证总和最小。
  • 最后判断一下总和是否小于 x 即可。

贪心的意思,反正每次都加上最小值,所以总和一定是最小的,当最小总和都大于 x 那么一定不存在合法情况!


见名知意,统计小于、小于等于、大于、大于等于的变量命名!

< \\lt < lt
≤ \\le le
> \\gt > gt
≥ \\ge ge


时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)


这种写法有一个坑点,当前 k 个数输入之后,ge 已经大于 n/2+1 了,那么 y 就不打印了,且 1 不应该打印 n/2-lt 个,而是打印 n-k 个即可,即把后面的补满就行了。

如果仍旧打印 n/2-lt 个的话,相当于将严格小于 y 的数的个数强行补充到 n/2 个,但实际上,中位数有重复情况出现。在这 WA 了好几次…

#include <bits/stdc++.h>

using namespace std;

int n, k, p, x, y;

int main() {
    scanf("%d%d%d%d%d", &n, &k, &p, &x, &y);
    int sum = 0, lt = 0, ge = 0;
    for (int i = 0; i < k; i ++ ) {
        int t;
        scanf("%d", &t);
        sum += t;      // 统计 k 个和
        
        if (t < y) lt ++ ;
        else ge ++ ;
    }
    
    int l = n / 2, r = n / 2 + 1;
    if (lt > l) {
        puts("-1");
        return 0;
    }
    
    int c1 = 0, cy = 0;   // 统计打印 1 打印 y 的次数
    if (ge >= r) sum += (n - k) * 1, c1 = n - k;  // 坑点,当ge大于r时,补上n-k个1即可,而不是n-lt个1
    else sum += (l - lt) * 1 + (r - ge) * y, c1 = l - lt, cy = r - ge;    // 补上1,补上y
    
    if (sum > x) puts("-1");
    else {
        while (c1 -- ) printf("%d ", 1);
        while (cy -- ) printf("%d ", y);
    }

    return 0;
}

mrk 大佬的代码依旧如此简洁!

优先补 1,强行给 y 找到 n/2 个严格小于等于它的数。如果填充多了,就说明中位数已经满足了,就不用填充 y 了。如果还有空,说明中位数还未满足,则后面的全部填充为 y 即可。

#include <iostream>
#include <cstdio>

#define pb push_back
#define fi first
#define se second
#define mp make_pair

using namespace std;

typedef long long LL;

template <typename T> void chkMax(T &x, T y) { if (y > x) x = y; }
template <typename T> void chkMin(T &x, T y) { if (y < x) x = y; }

const int N = 1e3 + 5;

int n, k, p, x, y, a[N], t;

int main() {
    scanf("%d%d%d%d%d", &n, &k, &p, &x, &y);
    LL s = 0;
    int c = 0;
    for (int i = 1; i <= k; i++) {
        scanf("%d", a + i);
        if (a[i] < y) c++;
    }
    t = k;
    if (c > n / 2) {
        puts("-1");
        return 0;
    }
    for (int i = 0; i < n / 2 - c; i++) a[++t] = 1;		// 优先补 1 
    while (t < n) {										// 不足补 y
        a[++t] = y;
    }
    for (int i = 1; i <= n; i++) s += a[i];
    if (s > x) {
        puts("-1");
        return 0;
    }
    for (int i = k + 1; i <= n; i++) printf("%d ", a[i]);
    return 0;
}

以上是关于[模拟] aw3664. 数组补全(模拟+贪心+中位数+思维)的主要内容,如果未能解决你的问题,请参考以下文章

[贪心] aw3705. 子集mex值(贪心+模拟+模板题)

[贪心] aw3686. 移动序列(贪心+模拟+思维)

[贪心] aw3769. 移动石子(模拟)

3664. 数组补全

[构造] aw3767. 最小的值(贪心+模拟)

[构造] aw3764. 三元数异或(贪心+模拟)