反悔贪心&局部调整法学习笔记
一、什么是反悔贪心
反悔贪心就是在普通贪心的过程中“反悔”,从而使得一些看似不太好贪心的题变成贪心可做题。
二、反悔贪心普遍流程
就是先使用一个好想的贪心策略,使用优先队列进行维护,然后如果在贪心时发现一个东西不能选,那你要考虑之前选的东西里面是否有比这个东西相对意义更差的东西,如果有就丢掉之前选的那个比较差的东西,把这个比较好的东西塞进优先队列里,这就是“反悔”。
三、反悔贪心框架
priority_queue<int>q;//这里默认大根堆,但有时候需要小根堆
开始贪心
{
if(这个东西可以选)
{
q.push(这个东西);
//增加计数,有些题目可能还需要增加其它东西
}
else if(q.size()&&队列里最不好的东西比这个东西差)
{
//增加计数,有些题目可能还需要增加其它东西
q.pop();//扔掉差的
q.push(这个东西);//扔进好的
}
}
注意:这只是板子,应用时请随机应变。
四、反悔贪心例题讲解
CF1974G Money Buys Less Happiness Now
反悔贪心模板题,首先如果你能增加幸福值就增加幸福值,如果你不能增加(没钱了)那就看看之前是在哪些月份获取了幸福值,找到价格最贵的那一个月,如果那一个月的价格比这个月的价格高,扔掉那一个月,加入这个月(因为这样会让钱变得更多,而且不会影响幸福值)。
代码:
#include<bits/stdc++.h>
using namespace std;
signed main()
{
int _;
scanf("%d",&_);
while(_--)
{
priority_queue<int>q;
int n,m,sum = 0,num = 0;
scanf("%d %d",&n,&m);
for(int i = 1;i<=n;i++,sum+=m)
{
int x;
scanf("%d",&x);
if(x<=sum)
{
sum-=x;
num++;
q.push(x);
}
else if(q.size()&&q.top()>x)
{
sum+=q.top()-x;
q.pop();
q.push(x);
}
}
printf("%d\n",num);
}
return 0;
}
CF1526C2 Potions (Hard Version)
首先如果读入的 \(x \ge 0\),那肯定是选的,因为不会对答案造成任何负面影响,然后如果 \(sum+x \ge 0\),虽然会让 \(sum\)(目前前缀和)变得更小,但是依旧不影响,所以也是可以直接选的,只不过这里得加入优先队列(因为前面大于等于 \(0\) 的数,对后面无法选择不会造成影响,没必要加入优先队列,因为后面如果无法选择那肯定是负数,负数不可能大于大于等于 \(0\) 的数),然后如果 \(sum+x<0\),就是无法选择,那么就看一下前面选的最差的负数有没有比这个差,如果比这个差就“反悔”。
代码:
#include<bits/stdc++.h>
using namespace std;
signed main()
{
int n;
long long sum = 0;
int num = 0;
scanf("%d",&n);
priority_queue<int,vector<int>,greater<int>>q;
for(int i = 1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(x>=0)
{
sum+=x;
num++;
}
else if(sum+x>=0)
{
sum+=x;
num++;
q.push(x);
}
else
{
if(q.size()&&q.top()<x)
{
sum+=x-q.top();
q.pop();
q.push(x);
}
}
}
printf("%d",num);
return 0;
}
CF1185C2 Exam in BerSU (hard version)
还是同样的套路,如果能选就选,不能选的话我们就反悔。
本题的输出在不能选的情况下得再准备一个优先队列,因为如果不能选的话,我们每次肯定得从优先队列里面取出最大的(最拖后腿的),放到这个临时的优先队列里,然后输出完后再放回去,然后在不能选的情况下输出的时候还要减 \(1\),因为你那个时候还没有将这个东西放入优先队列(你也可以先放,这样就不用减 \(1\) 了)。
代码:
#include<bits/stdc++.h>
using namespace std;
signed main()
{
priority_queue<int>q,tmp;
int n,m,sum = 0;
scanf("%d %d",&n,&m);
for(int i = 1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(sum+x<=m)
{
sum+=x;
q.push(x);
printf("%d ",i-q.size());
}
else
{
while(sum+x>m)
{
sum-=q.top();
tmp.push(q.top());
q.pop();
}
printf("%d ",i-q.size()-1);
while(tmp.size())
{
sum+=tmp.top();
q.push(tmp.top());
tmp.pop();
}
if(q.top()>x)
{
sum-=q.top()-x;
q.pop();
q.push(x);
}
}
}
return 0;
}
P2949 [USACO09OPEN] Work Scheduling G
同样的套路。首先得给这些任务按照时间顺序排序,然后开始贪心,依旧是能选的就选,不能选的就从优先队列里找到最差的看下有没有比当前的差,如果有就放入。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+5;
struct node
{
int d;
int p;
}a[N];
int cmp(node x,node y)
{
return x.d<y.d;
}
signed main()
{
priority_queue<int,vector<int>,greater<int>>q;
int num = 0,sum = 0;
int n;
scanf("%lld",&n);
for(int i = 1;i<=n;i++)
{
scanf("%lld %lld",&a[i].d,&a[i].p);
}
sort(a+1,a+n+1,cmp);
for(int i = 1;i<=n;i++)
{
if(num<a[i].d)
{
num++;
q.push(a[i].p);
sum+=a[i].p;
}
else if(q.top()<a[i].p)
{
sum+=a[i].p-q.top();
q.pop();
q.push(a[i].p);
}
}
printf("%lld",sum);
return 0;
}
CF865D Buy Low Sell High
由于只有一个股票,我们不确定一个股票如果买了要到什么时候买最好,我们可以贪心地选择,如果当前的优先队列里最便宜的股票价钱比当前这个股票价钱便宜,那么就“卖掉”这个股票(这样一定是对的,因为就算到后面再卖了赚的差价更高,我们之前卖出的价钱也会放到优先队列里,我们理论上是卖了那个股票,但是其实不仅能当最终结果,也可以当中继器,所以还可以和后面的股票进行交易,答案就不会出现问题),然后不管怎样,都得往优先队列里面放当前这个股票,因为它还有机会当中继器。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
priority_queue<int,vector<int>,greater<int>>q;
int n,sum = 0;
scanf("%lld",&n);
for(int i = 1;i<=n;i++)
{
int x;
scanf("%lld",&x);
if(q.size()&&q.top()<x)
{
sum+=x-q.top();
q.pop();
q.push(x);
}
q.push(x);
}
printf("%lld",sum);
return 0;
}
后面还会更新更多例题,敬请期待!!
五、什么是局部调整法
局部调整法就是在做选择性贪心时挑两个相邻的位置进行数学中的不等式分析,从而得出排序法则,同时,它还有一个重要的用处——判断选择性题目是否可以贪心。
六、局部调整法普遍推导过程
就是先定义两个相邻数 \(x,y\),然后假设其它位置的数固定,而 \(x,y\) 可以互换,那么我们分别写出 \(1,2,\dots,x,y,\dots,n-1,n\) 的贡献和 \(1,2,\dots,y,x,\dots,n-1,n\) 的贡献,然后假设 \(1,2,\dots,x,y,\dots,n-1,n\) 的贡献比 \(1,2,\dots,y,x,\dots,n-1,n\) 的贡献更优,然后这就是排序法则,当然,有时候这个排序法则比较麻烦,你可以对它进行化简。那局部调整法如何用来快速判断选择性题目是否可以贪心呢?很简单,只需要判断我们化简后的不等式是否满足偏序关系,偏序关系就是你用小的连大的不会成环,当然偏序关系还有纯数学的定义。
七、如何快速判断一个不太好判断的不等式是否满足偏序关系
- 手动造数据,然后根据这个不等式写排序方式,如果能排出来就满足偏序关系,拍不出来就说明不满足偏序关系(注意,此方法较唐)。
- 直接用偏序关系的三个性质验证,如果都满足就没啥问题了,如果不满足就有问题(注意:此方法也比较唐)。
两种方法虽然都不一定保证一定正确,但是合在一起正确率肯定高于百分之九十九,而且这也是算法竞赛中很好用的方法,当然,你应用时完全不需要合在一起,随便用一种方法就行了。
七、局部调整法例题讲解
P1012 [NOIP 1998 提高组] 拼数
首先先把两者的贡献写下来,组成不等式(这里 \(A\) 表示 \(x\) 以前的数拼在一起的结果,\(B\) 表示 \(x,y\) 后面的数拼在一起的结果):
\]
\]
\]
然后令 \(|DFG|\) 表示 \(D\) 和 \(F\) 和 \(G\) 拼起来的结果,那么:
\]
然后这个个排序规则排序就好了。
至于 \(a_x \times 10^{|a_y|}+a_y>a_y \times 10^{|a_x|}+a_x\) 为啥是偏序是因为:
\]
\]
\]
\]
发现一边全是 \(x\),另一边全是 \(y\),所以一定是偏序。
代码:
#include <bits/stdc++.h>
using namespace std;
string a[25];
string ans;
bool cmp(string a,string b)
{
string c = a+b,d = b+a;
return c>d;
}
int main()
{
int n;
cin >> n;
for(int i = 1;i<=n;i++)
{
cin >> a[i];
}
sort(a+1,a+n+1,cmp);
for(int i = 1;i<=n;i++)
{
ans+=a[i];
}
cout << ans;
return 0;
}
P5963 [BalticOI ?] Card 卡牌游戏【来源请求】
首先把两者的贡献写下来,组成不等式(假设有两组卡片 \((x_i,y_i),(x_j,y_j)\)):
\]
\]
\]
这不直接就搞定了吗,而且还是一边全是 \(j\),一边全是 \(i\) 的情况,所以一定是偏序。
注意:十年 OI 一场空,不开 long long 见祖宗。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 6e5+5;
struct node
{
int x;
int y;
int sum;
}a[N];
int cmp(node x,node y)
{
return x.sum<y.sum;
}
signed main()
{
int n;
scanf("%lld",&n);
for(int i = 1;i<=n;i++)
{
scanf("%lld %lld",&a[i].x,&a[i].y);
a[i].sum = a[i].x+a[i].y;
}
sort(a+1,a+n+1,cmp);
int ans = 0;
for(int i = 1;i<=n/2;i++)
{
ans+=min(a[i].x,a[i].y);
}
for(int i = n/2+1;i<=n;i++)
{
ans-=max(a[i].x,a[i].y);
}
printf("%lld",ans);
return 0;
}
反悔贪心&局部调整法学习笔记的更多相关文章
- Spring MVC 学习笔记9 —— 实现简单的用户管理(4)用户登录显示局部异常信息
Spring MVC 学习笔记9 -- 实现简单的用户管理(4.2)用户登录--显示局部异常信息 第二部分:显示局部异常信息,而不是500错误页 1. 写一个方法,把UserException传进来. ...
- Django学习笔记(14)——AJAX与Form组件知识补充(局部钩子和全局钩子详解)
我在之前做了一个关于AJAX和form组件的笔记,可以参考:Django学习笔记(8)——前后台数据交互实战(AJAX):Django学习笔记(6)——Form表单 我觉得自己在写Django笔记(8 ...
- tensorflow学习笔记——自编码器及多层感知器
1,自编码器简介 传统机器学习任务很大程度上依赖于好的特征工程,比如对数值型,日期时间型,种类型等特征的提取.特征工程往往是非常耗时耗力的,在图像,语音和视频中提取到有效的特征就更难了,工程师必须在这 ...
- swift学习笔记4——扩展、协议
之前学习swift时的个人笔记,根据github:the-swift-programming-language-in-chinese学习.总结,将重要的内容提取,加以理解后整理为学习笔记,方便以后查询 ...
- ASP.Net开发基础温故知新学习笔记
申明:本文是学习2014版ASP.Net视频教程的学习笔记,仅供本人复习之用,也没有发布到博客园首页. 一.一般处理程序基础 (1)表单提交注意点: ①GET通过URL,POST通过报文体: ②需在H ...
- 0028 Java学习笔记-面向对象-Lambda表达式
匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...
- C++学习笔记(3)
本学习笔记是C++ primer plus(第六版)学习笔记.是C++学习笔记(2)的后续.复习C++基础知识的可以瞄瞄. 转载请注明出处http://www.cnblogs.com/zrtqsk/p ...
- 【前端】移动端Web开发学习笔记【1】
下一篇:移动端Web开发学习笔记[2] Part 1: 两篇重要的博客 有两篇翻译过来的博客值得一看: 两个viewport的故事(第一部分) 两个viewport的故事(第二部分) 这两篇博客探讨了 ...
- 两千行PHP学习笔记
亲们,如约而至的PHP笔记来啦~绝对干货! 以下为我以前学PHP时做的笔记,时不时的也会添加一些基础知识点进去,有时还翻出来查查. MySQL笔记:一千行MySQL学习笔记http://www.cnb ...
- 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要三
最近工作较忙,手上有几个项目等着我独立开发设计,所以平时工作日的时候没有太多时间,下班累了就不想动,也就周末有点时间,今天我花了一个下午的时间来继续总结与整理书中要点,在整理的过程中,发现了书中的一些 ...
随机推荐
- Luogu P8112 [Cnoi2021] 符文破译 题解 [ 蓝 ] [ KMP ] [ 线性 dp ] [ 决策单调性 dp ]
符文破译:KMP + dp 的好题. 暴力 dp 不难打出一个暴力 dp:设计 \(dp_i\) 表示当前前 \(i\) 位全部完成了匹配,所需的最小分割数. 转移也是简单的,我们在 KMP 的过程中 ...
- 植物大战僵尸杂交版,最新安装包(PC+手机+苹果)+ 修改器+高清工具
植物大战僵尸杂交版:全新游戏体验与创意碰撞 游戏简介 <植物大战僵尸杂交版>是由B站知名UP主潜艇伟伟迷基于经典游戏<植物大战僵尸>进行的一次大胆且富有创意的二次创作.这款游戏 ...
- flutter - [01] Dart概述
题记部分 一.什么是dart dart是由谷歌开发的计算机编程语言,可以被用于web.服务器.移动应用和物联网等领域的开发 dart诞生于2011年,号称要取代JavaScript.但是过去的几年中一 ...
- 【攻防世界】warmup
warmup (反序列化与sql注入) 题目来源 攻防世界 NO.GFSJ0999 题目描述 题目提示:平平无奇的输入框 打开网址页面如下,没有有用信息. 题目给了附件,直接下载,得到源码如下: in ...
- 实测windows系统使用cmd搜索包含某个关键词的代码及PHP自动生成数据字典
1.windows系统下进入cmd窗口-->替换自己的路径,执行下面命令 findstr /s /i "carlist" D:\phpstudy_pro\WWW\wxx\*. ...
- pandas contains 函数
Series.str.contains( pat, # 要查询的字符串.要查询的或者正则表达式 case=True, # 是否对大小写敏感 flags=0, # 用来传给正则模块的参数,比如 flag ...
- Mysql导入数据的时候报错Unknown collation: 'utf8mb4_0900_ai_ci'什么问题?
最近从线上把数据导出来想搭建到本地的时候报了这么一个错? [ERR] 1273 - Unknown collation: 'utf8mb4_0900_ai_ci' 这个错误究竟是什么原因影响的呢? 是 ...
- uniapp 截屏扫码
最近开发功能遇到个需求,用户点击某个操作之后,需要截取当前屏幕内容,并扫码识别屏幕截图中的二维码,代码如下: 首先将代码抽离到外部文件中,以便复用: // 截图 export function tak ...
- 免费的编程连字等宽字体:Fira Code
免费的编程连字等宽字体:Fira Code 介绍和特征 介绍 Fira 是 Mozilla 公司 主推的字体系列.Fira Code 专为写程序而生,开源免费.除了具有等宽等基本属性外,还加入了编程连 ...
- python ImportError: libGL.so.1: cannot open shared object file: No such file or directory
前言 python 报错python ImportError: libGL.so.1: cannot open shared object file: No such file or director ...