首页 > 其他分享 >反悔贪心

反悔贪心

时间:2024-05-22 18:59:21浏览次数:25  
标签:const int res long 反悔 using 贪心 define

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;
}

标签:const,int,res,long,反悔,using,贪心,define
From: https://www.cnblogs.com/ZouYua/p/18206905

相关文章

  • 反悔贪心学习笔记
    本文仅用于笔者关于反悔贪心的学习笔记,反悔贪心是笔者在一场$div4$中遇到的问题,故来学习一番【学习笔记】反悔贪心 1、什么是反悔贪心?贪心本身是没有反悔操作的,贪心求的就是当前的最优解。但当前的最优解有可能是局部最优解,而不是全局最优解,这时候就要进行反悔操......
  • 挑战程序设计竞赛 2.2章习题 POJ - 3617 Best Cow Line 贪心
    FJ正准备带着他的N头奶牛(1≤N≤2,000)参加一年一度的“年度最佳农民”比赛。在这个比赛中,每个农民都会将他的奶牛排成一行,然后引导它们经过评委。今年比赛的组织者采用了一种新的注册方案:只需按照它们出现的顺序注册每头奶牛的首字母(即如果FJ带着Bessie、Sylvia和Dora依次出......
  • 贪心:重叠区间问题
    leetcode452,435假设有intervals[][]这么一个二维数组,我们要找到其中的重叠区间个数:解题思路:分两种情况讨论即可:首先我们需要对区间进行一个排序,为了尽量让相邻的区间重叠,以便后续操作1.如果当前区间的左边界和上个区间的右边界不重合,那么这两个区间肯定是......
  • 如果想得到线程中的反悔呢
    importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.*;publicclassMain{publicstaticvoidmain(String[]args){intcorePoolSize=5;//核心线程数intmaxPoolSize=10;//最大线程数longkeepAlive......
  • leedcode-分发饼干(贪心算法)
    自己写的,没有使用排序,会超出时间限制:classSolution:deffindContentChildren(self,g:List[int],s:List[int])->int:iflen(s)==0:return0count=0foriinrange(len(g)):mydict={}forjin......
  • 一些贪心的解题报告
    一些贪心的解题报告贪心题一般来说都是观察结论远简单于严谨证明,所以不会过多的去证明。1.Treecompass题目来源codeforcesdiv1934Chttps://codeforces.com/problemset/problem/1943/C题面翻译给定一棵节点数为\(n\)的树(\(1\len\le2\cdot10^3\)),一开始节点都是白色......
  • 说说你对贪心算法、回溯算法的理解?应用场景?
    一、贪心算法贪心算法,又称贪婪算法,是算法设计中的一种思想其期待每一个阶段都是局部最优的选择,从而达到全局最优,但是结果并不一定是最优的举个零钱兑换的例子,如果你有1元、2元、5元的钱币数张,用于兑换一定的金额,但是要求兑换的钱币张数最少如果现在你要兑换11元,按照贪心算法......
  • 36天【代码随想录算法训练营34期】第八章 贪心算法 part05( ● 435. 无重叠区间 ● 7
    435.无重叠区间classSolution:deferaseOverlapIntervals(self,intervals:List[List[int]])->int:count=0intervals.sort(key=lambdax:x[0])foriinrange(1,len(intervals)):ifintervals[i][0]<intervals[i-......
  • 35天【代码随想录算法训练营34期】第八章 贪心算法 part04 ( ● 860.柠檬水找零 ● 4
    860.柠檬水找零classSolution:deflemonadeChange(self,bills:List[int])->bool:amt_five=0amt_ten=0amt_twenty=0foriinbills:ifi==5:amt_five+=1elifi==10:......
  • 34天【代码随想录算法训练营34期】第八章 贪心算法 part03 (● 1005.K次取反后最大化
    1005.K次取反后最大化的数组和classSolution:deflargestSumAfterKNegations(self,nums:List[int],k:int)->int:nums.sort(key=lambdax:abs(x),reverse=True)foriinrange(len(nums)):ifnums[i]<0andk>0:......