反悔贪心

反悔贪心=贪心选择+堆调整

只可意会不可言传

更好的理解还是多做题,这里直接把各大佬整理的反悔贪心做一遍吧。

灵神贪心题单中的反悔贪心部分

LCP 30. 魔塔游戏

给定一数组,计算每个前缀和,要求每个结果都为非负数,过程中可以选择将任意数据移到数组末尾,问至少需要移动几次。
如果不管移动多少次都无法满足要求 返回-1

贪心的一直向后累加,如果前缀和出现负值,则此时必须要移动一个数据到末尾。
移动当前数据能否恢复为非负数?显然可以,由于是当前数据累加后导致出现的负值,所以移除必然可行。
是否是最优操作?明显不是,应当移除在此之前出现过的最小负数
比如数组[100,100,100,-200,-120,-120,200],当计算到-120时 前缀和为-20,此时应当放到末尾的是-200
否则如果移除-120,下一个碰到-120时发现还需要移除。
需要一个结构满足可以快速插入,并且获取所有插入数据的最值。显然这是

什么情况下不管移动多少次都无法满足要求?很明显最后一项的前缀和,也就是数组和本身必须是非负数。
如果数组和是非负数是否一定有解?显然是的,因为一定可以把所有正数放在前面,负数放在后面,保证过程中不会出现负数。

//核心代码模式
class Solution {
public:
    int magicTower(vector<int>& nums) {
        long sum = 0;
        for(auto v : nums) sum += v;
        if(sum < 0) return -1;
        priority_queue<int, vector<int>, greater<int>> pq;
        int ans = 0;
        long hp = 0;
        for(auto v : nums){
            hp += v;
            if(v < 0) pq.push(v);
            if(hp < 0){
                hp -= pq.top(); pq.pop();
                ans++;
            }
        }
        return ans;
    }
};

1642. 可以到达的最远建筑

给你一个整数数组 heights ,表示建筑物的高度。另有一些砖块 bricks 和梯子 ladders 。
你从建筑物 0 开始旅程,如果下个建筑高于当前建筑 则需要使用砖块或者梯子,其中梯子可以确保通过,而砖块需要填充高度差的数量。
计算最远到达的下标。

核心思路是在移动过程中,高度差较大的位置必须使用梯子,较小的位置使用砖块填充。
一种维护方式是,使用小根堆维护前ladders个高度差,如果超过ladders,则多余的部分用砖块填充。
另一种维护方式,使用大根堆,始终用砖块填充,一旦发现无法满足,则pop最大的高度差 改为梯子。

630. 课程表 III

这里有 n 门不同的在线课程,按从 1 到 n 编号。
给你一个数组 courses ,其中 courses[i] = [durationi, lastDayi]
表示第 i 门课将会 持续 上 durationi 天课,并且必须在不晚于 lastDayi 的时候完成。
你的学期从第 1 天开始。且不能同时修读两门及两门以上的课程。
返回你最多可以修读的课程数目。

按最晚时间排序 务必充分理解题目的设定

class Solution {
public:
    int scheduleCourse(vector<vector<int>>& courses) {
        priority_queue<int> pq;
        sort(courses.begin(), courses.end(), [](const vector<int>& A, const vector<int>& B)->bool{return A[1] < B[1];});
        int sum = 0;
        for(auto v : courses){
            pq.push(v[0]);
            sum += v[0];
            if(sum > v[1]) sum -= pq.top(), pq.pop();
        }
        return pq.size();
    }
};

871. 最低加油次数

  • 虚拟终点
  • auto&v 遍历写法
class Solution {
public:
    int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
        stations.push_back({target, 0});
        priority_queue<int> pq;
        int pre = 0;
        int ans = 0;
        for(auto& v : stations){
            startFuel -= v[0] - pre;
            while(!pq.empty() && startFuel < 0){
                startFuel += pq.top(); pq.pop();
                ans++;
            }
            if(startFuel < 0) return -1;
            pre = v[0];
            pq.push(v[1]);
        }
        return ans;
    }
};

3362. 零数组变换 III

给你一个长度为 n 的整数数组 nums 和一个二维数组 queries ,其中 queries[i] = [li, ri] 。
每一个 queries[i] 表示对于 nums 的以下操作:
将 nums 中下标在范围[li, ri] 之间的每一个元素最多减少1
坐标范围内每一个元素减少的值相互独立
零数组 指的是一个数组里所有元素都等于 0 。
请你返回最多可以从 queries 中删除多少个元素,使得 queries 中剩下的元素仍然能将 nums 变为一个零数组。如果无法将 nums 变为一个零数组,返回 -1 。

枚举每个位置,如果不满足条件,则需要选择向右延伸最远的区间。
每个左端点<=当前位置的区间都要入堆
出堆最远区间直到当前位置满足条件,这个表示选择这个区间
差分维护

class Solution {
public:
    int maxRemoval(vector<int>& nums, vector<vector<int>>& queries) {
        int n = nums.size(), m = queries.size();
        sort(queries.begin(), queries.end(), [](const vector<int>& A, const vector<int>& B)->bool{return A[0] < B[0];});
        vector<int> diff(n + 1);
        int sum = 0;
        priority_queue<int> pq;
        for(int i = 0, j = 0; i < n; i++){
            sum += diff[i];
            while(j < m && queries[j][0] <= i){
                pq.push(queries[j][1]);
                j++;
            }
            while(sum < nums[i] && !pq.empty() && pq.top() >= i){
                sum++;
                diff[pq.top() + 1]--;
                pq.pop();
            }
            if(sum < nums[i]) return -1;
        }
        return pq.size();
    }
};

2813. 子序列最大优雅度

给你一个长度为 n 的二维整数数组 items 和一个整数 k 。
items[i] = [profiti, categoryi],其中 profiti 和 categoryi 分别表示第 i 个项目的利润和类别。
现定义 items 的 子序列 的 优雅度 可以用 \(total_profit + distinct_categories^2\) 计算
你的任务是从 items 所有长度为 k 的子序列中,找出 最大优雅度 。

  • 只有某个类型个数超过1 才能入堆 作为待替换项
  • 按profit排序 后续只有新增类型才可能形成更大的优雅度
using i64 = long long;
class Solution {
public:
    long long findMaximumElegance(vector<vector<int>>& items, int k) {
        int n = items.size();
        sort(items.begin(), items.end(), [](vector<int>& A, vector<int>& B)->bool{return A[0] > B[0];});
        map<int, int> count;
        i64 ans = 0, mx = 0, cc = 0;
        priority_queue<vector<int>, vector<vector<int>>, decltype([](vector<int>& A, vector<int>& B)->bool{return A[0] > B[0];})> pq;
        for(int i = 0; i < k; i++){
            auto& v = items[i];
            mx += v[0];
            if(!count.contains(v[1])) cc++;
            count[v[1]]++;
            if(count[v[1]] > 1) pq.push(v);
        }
        mx += cc * cc;
        ans = mx;
        for(int i = k; i < n; i++){
            auto& v = items[i];
            if(pq.empty()) break;
            if(count.contains(v[1])) continue;
            mx += cc * 2 + 1; //(c + 1)^2 - c^2
            mx += v[0] - pq.top()[0]; //cur - old
            cc++;
            pq.pop();
            count[v[1]]++;
            ans = max(ans, mx);
        }
        return ans;
    }
};

3049. 标记所有下标的最早秒数 II
有难度

  • 首先是二分
  • 然后注意只有某个下标首次出现的位置是有效位置
  • 倒序遍历

2463. 最小移动总距离

  • DP
    应该说是DP好题
  • 贪心
    这到底怎么贪?
  • 最小费用最大流
//C#代码+模板
public class Solution {
    public long MinimumTotalDistance(IList<int> robot, int[][] factory) {
        int n = robot.Count, m = factory.Length;
        MCMF_Dinic g = new MCMF_Dinic(n + m + 2);
        int s = 0, t = n + m + 1;
        for (int i = 1; i <= n; i++) g.AddEdge(s, i, 1, 0);
        for (int i = 1; i <= m; i++) g.AddEdge(i + n, t, factory[i - 1][1], 0);
        for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) g.AddEdge(i + 1, j + n + 1, 1, Math.Abs(factory[j][0] - robot[i]));
        return (g.Dinic(s, t).Item2);
    }
}

2599. 使前缀和数组非负
会员题 但是同 LCP 30. 魔塔游戏 一模一样

蒟蒻lndjy的反悔贪心题单

简单题都是相似的 排序 堆维护

P2949 [USACO09OPEN] Work Scheduling G

N项工作 每项包含截止日期和收益,每天只能完成一项,问最大收益

小跟堆,保持前缀天数内堆大小<=天数。

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
    int n; cin >> n;
    vector<vector<i64>> vv;
    for (int i = 0; i < n; i++) {
        int d, p; cin >> d >> p;
        vv.push_back({ d, p });
    }
    sort(vv.begin(), vv.end(), [](vector<i64>& A, vector<i64>& B)->bool {return A[0] < B[0]; });
    priority_queue<i64, vector<i64>, greater<i64>> pq;
    for (auto& v : vv) {
        pq.push(v[1]);
        if (pq.size() > v[0]) pq.pop();
    }
    i64 ans = 0;
    while (!pq.empty()) ans += pq.top(), pq.pop();
    cout << ans << endl;
}

P4053 [JSOI2007] 建筑抢修

大根堆,累计已使用天数,超过则pop最大的。

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
    int n; cin >> n;
    vector<vector<i64>> vv;
    for (int i = 0; i < n; i++) {
        i64 t1, t2; cin >> t1 >> t2;
        vv.push_back({ t1, t2 });
    }
    sort(vv.begin(), vv.end(), [](vector<i64>& A, vector<i64>& B)->bool {return A[1] < B[1]; });
    priority_queue<i64> pq;
    i64 total = 0;
    for (auto& v : vv) {
        pq.push(v[0]);
        total += v[0];
        if (total > v[1]) total -= pq.top(), pq.pop();
    }
    cout << pq.size() << endl;
}

小Z的AK计划

Buy Low Sell High

[POI2012] HUR-Warehouse Store

种树

[国家集训队] 种树

Olympiad in Programming and Sports

[AGC018C] Coins

[APIO/CTSC2007] 数据备份

BACKUP - Backup Files

April Fools' Problem (medium)

April Fools' Problem (hard)

Cardboard Box

P6821 [PA 2012 Finals] Tanie linie

给定长度为n的数组 求至多k个不相交子段的和的最大值

k-Maximum Subsequence Sum

[NOI2019] 序列

posted @ 2025-01-23 02:17  云上寒烟  阅读(50)  评论(0)    收藏  举报