贪心 学习笔记

概述

贪心算法指每一步都选当前的最优解,不考虑这一步对后面的影响。使用时需要先证明贪心策略的正确性。一般使用反证法或数学归纳法证明。

例题

P1080

这一题要求的是一个最优的排列,考虑反证。假设当前的排列最优,尝试交换相邻两个大臣生成一个新的排列。

设前面所有人左手的乘积为 \(S\)。交换前两个人的最大值是 \(\max(\frac{S}{b_i},\frac{Sa_i}{b_{i+1}})\),交换后为 \(\max(\frac{S}{b_i+1},\frac{Sa_{i+1}}{b_{i}})\)。如果交换后更劣,有:

\[\begin{aligned}\max(\frac{S}{b_i},\frac{Sa_i}{b_{i+1}})&<\max(\frac{S}{b_{i+1}},\frac{Sa_{i+1}}{b_{i}})\\\max(\frac{1}{b_i},\frac{a_i}{b_{i+1}})&<\max(\frac{1}{b_{i+1}},\frac{a_i+1}{b_{i}})\\\max(b_{i+1},a_ib_i)&<\max(b_i,a_{i+1}b_{i+1})\\a_ib_i\leq a_{i+1}b_{i+1}\end{aligned} \]

按照这个式子排序再统计答案即可。

反悔贪心

P2949

容易想到一个简单的贪心策略:优先找截止时期靠前的做。

但是可以 hack:3 1 1 2 114514 2 1919810。可能在某一时刻有很多高利润的任务截止,只能挑一个做。

这时可以反悔,将之前做的利润最低的任务退掉做这一个任务。用一个堆维护做过的任务即可。

#include<bits/stdc++.h>
using namespace std;
int n;
long long ans;
pair<int,int>a[100005];
priority_queue<int,vector<int>,greater<int> >q;
int main(){
  cin>>n;
  for(int i=1;i<=n;i++)cin>>a[i].first>>a[i].second;
  sort(a+1,a+n+1);
  for(int i=1;i<=n;i++){
    if(q.size()<a[i].first)ans+=a[i].second,q.push(a[i].second);
    else (q.top()<a[i].second)&&(ans+=(a[i].second-q.top()),q.pop(),q.push(a[i].second),0);
  }
  return cout<<ans<<'\n',0;
}

双向链表

P1484

还是先举一个简单的贪心策略,优先挑获利最大的种。

hack:4 3 4 3 1。如果按照上面的策略,选出的是 \(4+1=5\),但是答案是 \(3+3=6\),因为选 \(4\) 比选相邻的 \(3+3\) 劣。

可以设计一个反悔机制,在选取了某个点后,可以把这个点退掉,改为选旁边两个点。在选了一个点后,把旁边两个点删除,而把这个点的获利改为旁边两个点的获利减这个点的获利。这个过程可以用双向链表维护。

如果再选一遍这个点,变成了这样:

此时 \(2\sim6\) 都不能选,收益是 \(v_4+(v_3+v_5-v_4)=v_3+v_5\)。这和选 \(3,5\) 的情况是一样的,相当于把 \(4\) 退掉而选了 \(3,5\)

#include<bits/stdc++.h>
using namespace std;
int n,k;
bool vis[500005];
long long ans;
struct node{
  long long v;
  int l,r;
}a[500005];
priority_queue<pair<long long,int>>q;
int main(){
  cin>>n>>k;
  for(int i=1;i<=n;i++)cin>>a[i].v,a[i].l=i-1,a[i].r=i+1,q.push(make_pair(a[i].v,i));
  for(int i=1;i<=k;i++){
    while(vis[q.top().second])q.pop();
    int now=q.top().second;
    if(q.top().first<=0)break;
    ans+=q.top().first,q.pop(),vis[a[now].l]=vis[a[now].r]=1;
    a[now].v=a[a[now].l].v+a[a[now].r].v-a[now].v;
    q.push(make_pair(a[now].v,now));
    a[now].l=a[a[now].l].l,a[now].r=a[a[now].r].r;
    a[a[now].l].r=a[a[now].r].l=now;
  }
  cout<<ans<<'\n';
  return 0;
}

[[杂项]]

posted @ 2024-03-01 09:42  lgh_2009  阅读(1)  评论(0编辑  收藏  举报