1
希望对你有帮助.|

zouyua

园龄:1年10个月粉丝:3关注:3

反悔贪心

1. 介绍

贪心:即考虑局部最优解达到全局最优解的目的,但有时局部最优解并不会达到全局最优解,此时有两种思考方向,dp和反悔贪心

dp:能全局计算答案,根据拓扑学的DAG实现状态转移达到最优(这里不过多介绍)
反悔贪心:根据之前的决策进行反悔操作,主要用反悔堆实现去除性价比最低的决策,达到最优解

2.例题

1.工作调度

因为本题的完成时间是1,即可以根据截止时间判断前面有无安排满,此时性价比是价格,最低性价比是价格最低的,用小根堆存储,假设当前价格是p,判断是否比堆顶大,假设大,说明选这个比选堆顶的结果大,差值为增加的结果,后把堆顶删去,当前价值加入堆顶代表选了这个价值的工作,方便后面工作判断

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<int,int>;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define lowbit(x) (x) & (-x)
#define endl "\n" 
#define pb push_back
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=998244353;

void solve()
{
    int n; cin >> n;
    vector<ll> d(n + 1), p(n + 1);
    vector<PII> pr(n + 1);
    ll res = 0;
    map<ll, ll> mp;
    for(int i = 1; i <= n; i ++) {
        cin >> d[i] >> p[i];
        pr[i] = {d[i], p[i]};
    }
    sort(pr.begin() + 1, pr.end());
    priority_queue<ll, vector<ll>, greater<>> q;
    for(int i = 1; i <= n; i ++){
        if(pr[i].first > q.size()) {
            res += pr[i].second;
            q.push(pr[i].second);

        } else {
            if(pr[i].second > q.top()) {
                res -= q.top();
                res += pr[i].second;
                q.pop();
                q.push(pr[i].second);
            }
        }
    }
    //for(auto [u, v] : mp) res += v;
    cout << res << endl;
}
signed main()
{
    int T = 1;
    //cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}
--这里提供另一种思路,树状数组加二分,按价值从大到小排序,在当前截止时间之前未满都可以,贪心的从截止时间前往最前排,因为是按价值排序,所以不会出现替换的情况,且(1 <= n <= 1e5),所以上限截止到1e5即可(每个人物只占了一秒)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<int,int>;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define lowbit(x) (x) & (-x)
#define endl "\n" 
#define pb push_back
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=998244353;
struct BIT
{
    vector<int> tree;
    int N;
    BIT() {}
    BIT(int n){init(n);}
    void init(int n) {N = n, tree.resize(n);}
    void update(int x, int k){
        while (x < tree.size()) {
            tree[x] += k;
            x += x & -x;
        }
    }
    int query(int x){
        int res = 0;
        while (x > 0) {
            res += tree[x];
            x &= x - 1;
        }
        return res;
    }
};
void solve()
{
    int n; cin >> n;
    vector<ll> d(n + 1), p(n + 1);
    vector<PII> pr(n + 1);
    ll res = 0;
    map<ll, ll> mp;
    for(int i = 1; i <= n; i ++) {
        cin >> d[i] >> p[i];
        d[i] = min(d[i], (int)1e5);
        pr[i] = {d[i], p[i]};
    }
    sort(pr.begin() + 1, pr.end(), [](PII x, PII y) {
        return x.second > y.second;
    });
    BIT bit(100010);
    for(int i = 1; i <= n; i ++) {
        auto [d, p] = pr[i];
        if(bit.query(d) != d) {
            int l = 1, r = d;
            while(l < r) {
                int mid = l + r + 1 >> 1;
                if(bit.query(d) - bit.query(mid - 1) != d - mid + 1) l = mid;
                else r = mid -1;
            }
            bit.update(l, 1);
            res += p;
        }
    }
    //for(auto [u, v] : mp) res += v;
    cout << res << endl;
}
signed main()
{
    int T = 1;
    //cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

2.Money Buys Less Happiness Now

题意:开始钱为0,n个月,每月会增加x元,每件商品都有 ai元,在n月内能买多少件

把每次的花费看作性价比,性价比地的就是价格最大的,即用大根堆维护,now代表当前的钱的变化

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<int,int>;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define lowbit(x) (x) & (-x)
#define endl "\n" 
#define pb push_back
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=998244353;

void solve()
{
    int n, x; cin >> n >> x;
    vector<int> a(n + 1);
    for(int i = 1; i <= n; i ++) {
        cin >> a[i];
    }
    priority_queue<ll>q;
    ll res = 0, now = 0;
    for(int i = 1; i <= n; i ++) {
        if(now >= a[i]) {
            res ++;
            q.push(a[i]);
            now -= a[i];
        } else if(q.size() && q.top() >= a[i]) {
            now += q.top();
            now -= a[i];
            q.pop();
            q.push(a[i]);
        }
        now += x;
    }
    cout << res << endl;
}
signed main()
{
    int T = 1;
    cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

3. 建筑抢修

题意:我们再来考虑这样一个问题,我们有 n 个任务,并且每个任务都有两个属性——截止日期t2和完成耗时t1。在截止日期前完成任务就可以得到这个任务产生的价值。在同一时间我们只能去做一个任务。所有任务的价值都是一样的,问我们最后最多能完成多少个任务。

可以以截至时间排序,now表示当前时间的变化,此时性价比是t1耗时,最低性价比是耗时最大,用大根堆维护,如果now比t2小,直接加上这个贡献放入大根堆,否则此时耗时置换一下堆顶,相当于减去多的耗时,更新now时间

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<int,int>;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define lowbit(x) (x) & (-x)
#define endl "\n" 
#define pb push_back
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=998244353;
struct node {
    ll st, lst, ed;
    bool operator < (const node &_)  const  {
        return ed < _.ed;
    }
};
void solve()
{
    int n; cin >> n;
    vector<node> p(n + 1);
    for(int i = 1; i <= n; i ++) {
        ll t1, t2; cin >> t1 >> t2;
        p[i] = {t2 - t2, t1, t2};
    }
    ll res = 0;
    sort(p.begin() + 1, p.end());
    priority_queue<ll> q;
    ll now = 0;
    for(int i = 1; i <= n; i ++) {
        auto [st, lst, ed] = p[i];
        if(now + lst <= ed) {
            res ++;
            q.push(lst);
            now += lst;
        } else if(now + lst > ed && q.size()) {
            if(q.top() > lst) {
                now -= q.top();
                now += lst;
                q.pop();
                q.push(lst);
            }
        }
    }
    cout << res << endl;

}
int main()
{
    int T = 1;
    //cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

4.买卖股票

题意:已知接下来n天的股票价格,每天可以买入当天的股票,卖出已有的股票,或者什么都不做,求n天之后最大的利润。

考虑每一天的股票,如果可以i卖出,则选择之前价格最低的股票j买入 且 ai > aj 考虑用小根堆维护,注意这里有个关系 Csell - Cbuy = (Csell - Ci) + (Ci - Cbuy) Ci 代表任意一天的股票· 我们买价格最小的股票去卖价格最大的股票,以期得到最大的利润。我们先把当前的价格放入小根堆一次(这次是以上文的贪心策略贪心),判断当前的价格是否比堆顶大,若是比其大,我们就将差值计入全局最优解,再将当前的价格放入小根堆(这次是反悔操作)。这种传递关系代表我们把当前的股票价格若不是最优解,就没有用,最后可以得到全局最优解。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<int,int>;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define lowbit(x) (x) & (-x)
#define endl "\n" 
#define pb push_back
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=998244353;

void solve()
{
    int n;  cin >> n;
    vector<ll> a(n + 1);
    for(int i = 1;i <= n; i ++) cin >> a[i];
    priority_queue<ll, vector<ll>, greater<>> q;
    ll cnt = 0, res = 0;
    for(int i = 1; i <= n; i ++) {
        q.push(a[i]);
        if(q.size() && q.top() < a[i]) {
            cnt ++;
            res += a[i] - q.top();
            q.pop();
            q.push(a[i]);
        }
    }
    cout << res << endl;
}
int main()
{
    int T = 1;
    //cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

本文作者:zouyua

本文链接:https://www.cnblogs.com/ZouYua/p/18206905

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   zouyua  阅读(24)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起