CF2085D Serval and Kaitenzushi Buffet

Part1:思路

这题虽然简单,但是反应的思维方式比较有意义。

首先,去刻画什么情况下一盘寿司能被选择——显然是后面至少有 \(k\) 分钟空着没事做。

这里的关键在于 后面 二字,但是后面的情况如何正着做是不知道的。换句话说,前面数的选择与否只与后面的情况有关,所以我们从后往前考虑。

遇到这种选择物品题,我们往往第一反应都会是贪心。从后往前扫的时候,假若现在是第 \(n-k\) 分钟,那么后面恰好有 \(k\) 分钟空余,我们将选择当前这盘寿司。

但是这里显然不满足最优子结构。考虑刻画非最优的情况。具体的,对于第 \(1\sim n-k-1\) 中任意一盘没选中的寿司,假若选择它会带来更大美味值,那么我们都可以使用后面的寿司替换第 \(n-k\) 盘。

更加广泛的,对于任意 \(j<i\),我们都可以使用第 \(j\) 盘寿司替换第 \(i\) 盘。这时候就得到了一个反悔贪心,对于其正确性此时再稍作归纳便可得知。

Part2:代码

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define Sz(v) ((int)(v).size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
constexpr int N = 2e5 + 10;
int n, k, d[N];
multiset<int> s1;
void Solve() {
    scanf("%d %d", &n, &k);
    FL(i, 1, n) {
        scanf("%d", &d[i]);
    }
    s1.clear();
    ll ans = 0;
    FR(i, n, 1) {
        int lim = (n - i + 1) / (k + 1);
        if (Sz(s1) < lim) {
            ans += d[i];
            s1.emplace(d[i]);
        } else {
            if (!s1.empty() && *s1.begin() < d[i]) {
                ans -= *s1.begin();
                s1.erase(s1.begin());
                ans += d[i];
                s1.emplace(d[i]);
            }
        }
    }
    printf("%lld\n", ans);
}
int main() {
    int T = 1;
    scanf("%d", &T);
    for (; T--; Solve());
    return 0;
}
posted @ 2025-04-04 13:07  徐子洋  阅读(15)  评论(0)    收藏  举报