CF1400E Clear the Multiset 题解 贪心+分治

题目链接:http://codeforces.com/problemset/problem/1400/E

题目大意:

给定一个长度为\(n\)数列\(\{a_n\}\),你可以进行如下操作:

  • 操作1:任意选择一个区间 \([l,r]\),使区间内的每一个数减 \(1\)
  • 操作2:任意选择一个点 \(p\) 和一个正整数 \(x(x \ge 1)\),使 \(a_p\) 减去 \(x\)

求把原数列全部变为\(0\)的最少的操作次数。

\(1\le n\le 5000\)\(0\le a_i\le 10^9\)

解题思路

如果不选择操作1,只选择操作2,则 \(r - l + 1\) 次操作2就能够把所有数都清零。

如果执行操作1,则需要终点考虑一下。

当执行至少一次操作1的时候

首先,对于一个区间 \([l, r]\),只有满足所有元素都 \(\ge 1\) 时才能执行操作1。

其次,如果这个区间在执行若干次操作1后,所有的数字都仍然 \(\ge 1\),则其实没有带来任何效果。

所以操作1必然导致 —— 区间内最小的数字(假设为原先值为 \(x\))变为了 \(0\)\(\Rightarrow\) 很明显需要进行 \(x\) 次操作。

进行完操作后就又变成了(至少)两个部分。假设原先的最小值 \(x\) 对应的下标为 \(p\),则区间变成了两部分 \([l, p-1]\)\(p+1, r\),然后就可以对两个子区间再进行同样的操作。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5050;
int n, a[maxn];

int dfs(int l, int r) {
    if (l > r) return 0;
    int p = l;
    for (int i = l+1; i <= r; i++)
        if (a[i] < a[p])
            p = i;
    int x = a[p];
    if (x)
        for (int i = l; i <= r; i++) a[i] -= x;
    return min(r-l+1, dfs(l, p-1) + dfs(p+1, r) + x);
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    cout << dfs(1, n) << endl;
    return 0;
}
posted @ 2023-01-31 17:10  quanjun  阅读(34)  评论(0编辑  收藏  举报