吞下星星的少年·-·头像
关注

[蓝桥杯 2026 省 C] 回收处理(堆,前后缀,枚举)

题目链接:P16276 [蓝桥杯 2026 省 C] 回收处理 - 洛谷

题目描述:给定3*n个部件,并且依次排列,每个部件的价值为a[i]:若执行回收,该标价即为收益;若执行处理,该标价则计为成本。

小蓝的任务是从这 3N 个部件中挑选出两批特定组别:

  1. 回收组:挑选出恰好 N 个部件进行回收,总收益记为 R。
  2. 处理组:挑选出恰好 N 个部件进行处理,总成本记为 C。

挑选过程必须遵守严格的先后顺序:任何一个被回收的部件,其原始位置都必须早于所有被处理的部件(即若把回收部件的下标记为 p1​<p2​<⋯<pN​,处理部件的下标记为 q1​<q2​<⋯<qN​,则必须满足 pN​<q1​)。

在满足上述顺序的前提下,水线上剩下的 N 个部件将被直接弃置,不产生任何收益或成本。

现在,小蓝希望通过合理的方案,使得回收的总收益减去处理的总成本(R−C)尽可能大。对此,请你计算出这个差值的最大可能结果。

题目思路:我们需要求(R−C)最大,那么就是R尽可能大,C尽可能小,但是这里有一个限制,就是处理组必须在回收组后面,那么我们可以枚举每一个分界点i,对于[1,i]选取N个元素使得R最大,对于[i+1,3*N]选取N个元素使得R最小,其中i的范围是[N,2*N],因为要至少保留N个元素可选,现在我们可以通过堆来贪心处理[1,i]中最小的N个元素和[i+1,3*N]中最大的N个元素,具体的,我们可以每次尝试加入a[i]到堆里,比较堆顶和a[i]的大小就行了(当堆中元素个数超过N时),就能预处理出[1, i]中选n个数的最大或者最小值(当堆中元素个数等于N时);后缀也是类似的道理,预处理前缀,后缀,再按上面的办法枚举就行了

代码如下:

#include <bits/stdc++.h>
using namespace std;
using in128 = __int128_t;
#define int long long
const int INF = 4e18; // 定义一个很大的数,用于初始化
void solve()
{
    int N;
    cin >> N;
    int M = 3 * N;
    vector<int> a(M + 1);
    for (int i = 1; i <= M;i++){
        cin >> a[i];
    }
    // preMax[i]:表示从 [1, i] 中选出 N 个数的最大和
    // sufMin[i]:表示从 [i+1, M] 中选出 N 个数的最小和
    vector<int> preMax(M + 2, -INF); // 初始化为负无穷
    vector<int> sufMin(M + 2, INF);  // 初始化为正无穷
    //从左往右扫描,计算 preMax[i]维护前 i 个数中最大的 N 个数的和 使用小根堆(堆顶是最小的),堆里始终保留最大的 N 个数
    priority_queue<int, vector<int>, greater<int>> minHeap; // 小根堆
    int sum = 0;//当前堆中所有元素和
    for (int i = 1; i <= 2*N;i++){
        minHeap.push(a[i]);//踢掉堆顶(最小值)
        sum += a[i];
        if((int)minHeap.size()>N){
            sum -= minHeap.top();
            minHeap.pop();
        }
        // 当堆里恰好有N个元素时,记录preMax[i];
        if ((int)minHeap.size() == N){
            preMax[i] = sum;
        }
        // 如果不够 N 个,preMax[i] 保持 -INF(表示不合法)
    }
    // 第二步:从右往左扫描,计算 sufMin[i] 维护从 i+1 到 M 中最小的 N 个数的和使用大根堆(堆顶是最大的),堆里始终保留最小的 N 个数
    priority_queue<int> maxHeap;
    sum = 0;
    for (int i = M; i >= N+1;i--){
        maxHeap.push(a[i]);
        sum += a[i];
        // 如果堆中元素超过 N 个,就踢掉最大的那个
        // 这样堆里就始终保留着从 i 到 M 中最小的 N 个
        if ((int)maxHeap.size() > N)
        {
            sum -= maxHeap.top(); // 踢掉堆顶(最大值)
            maxHeap.pop();
        }

        // 当堆里恰好有 N 个元素时,记录 sufMin[i]
        if ((int)maxHeap.size() == N)
        {
            sufMin[i] = sum;
        }
        // 如果不够 N 个,sufMin[i] 保持 INF(表示不合法)
    }
    // 第三步:枚举分界点 i 回收组在[1, i] 中选 N 个,处理组在[i + 1, M] 中选 N 个 需要保证左边至少有 N 个,右边也至少有 N 个 所以 i 的范围是[N, 2N]
    int ans = -INF;
    for (int i = N; i <= 2 * N; i++)
    {
        // 回收收益 - 处理成本
        // 处理成本 = 选中的 N 个 a[i] 之和,所以贡献为负
        ans = max(ans, preMax[i] - sufMin[i + 1]);
        
    }
    cout << ans << "\n";
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    //cin >> T;
    while (T--)
    {
        solve();
    }

    return 0;
}

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2501_94316951/article/details/164632996

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--