当前位置: 首页 > news >正文

反悔贪心 系列

2025 CSPS1 的选择题 T15,就是这样一道题。

其实本来应当是 Slope Trick 来做的题目,但是聪明的人类找到了用优先队列维护当前最优解的 O(nlog⁡n)O(n\log n)O(nlogn) 做法。使得一些看起来很唬人的题目得以以惊人的小码量被解决,并且优于朴素 dp。

我的题单。大部分题目可以用相似的思路来做:

具体地,这类问题求可以选定元素的最大价值。每个元素都有价值和限制,价值总是和限制相关,即价值的叠加会影响限制。

先按照限制从小到大排序,然后依次插入每个元素,用优先队列维护已经插入的元素。当遇到违反限制、无法插入元素时,就判断当前元素能否替换队头。能就替换,且替换操作相对于全局而言要尽量是优的。

1.CF1526C Potions

题意

nnn 个药水排成一行,药水 111 在最左边,药水 nnn 在最右边。每个药水喝下后会使你的生命值增加 aia_iaiaia_iai 可能为负数,表示该药水会减少你的生命值。

你初始时生命值为 000,并且你会从左到右依次经过每个药水。每到一个药水处,你可以选择喝下它或者忽略它。你必须保证你的生命值始终不为负数。

你最多能喝下多少瓶药水?

CF1526C1 - Easy Version:1≤n≤20001\le n\le 20001n2000ai∈[−109,109]a_i\in[-10^9,10^9]ai[109,109]

CF1526C2 - Hard Version:1≤n≤2×1051\le n\le 2\times 10^51n2×105ai∈[−109,109]a_i\in[-10^9,10^9]ai[109,109]

思路

Easy Version 可以用 01 背包实现。设 fi,jf_{i,j}fi,j 表示前 iii 瓶药水喝了 jjj 瓶,获得的最大生命值。显然有转移:
fi,j=max⁡(fi−1,j,fi−1,j−1+ai)f_{i,j}=\max(f_{i-1,j},f_{i-1,j-1}+a_i)fi,j=max(fi1,j,fi1,j1+ai)

然后从 nnn 倒推,第一个 fn,j≥0f_{n,j}\ge 0fn,j0jjj 就是能喝下的最多药水瓶数。时间复杂度 O(n2)O(n^2)O(n2),代码略。但是完全过不了 Hard Vesion 啊!

考虑 nnn 瓶药水扫过去,贪心地想要每瓶药水都喝。当喝到一瓶让生命值 <0<0<0 的药水时,我们考虑用这瓶药水替换前面选过的某瓶药水,以实现当前药水瓶数尽可能多且生命值尽量大。

用小根堆维护选过的药水,队头是所选药水最小值,curcurcur 是当前生命值且保证 cur≥0cur\ge 0cur0 总是成立。若 cur+a<0cur+a<0cur+a<0,但是 a>q.topa>q.topa>q.top,用 cur←cur−q.top+acur\leftarrow cur-q.top+acurcurq.top+a 依然能使得 cur≥0cur\ge0cur0,虽然瓶数不变但是 curcurcur 更大了——这就是“反悔操作”。然后将 aaa 入队变为已选。

发现整个操作下来“非常人性化”,似乎无法被 hack。其实这个“反悔操作”,和 dp 的转移是等价的。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=5005;
ll n,a;
priority_queue<ll,vector<ll>,greater<ll> >q;
int main()
{scanf("%lld",&n);ll cur=0,ans=0;for(int i=1;i<=n;i++){scanf("%lld",&a);if(cur+a>=0){cur+=a;ans++;q.push(a);}else {if(q.empty())continue;ll tem=q.top();if(a>tem){cur=cur-tem+a;q.pop();q.push(a);}}}printf("%lld",ans);return 0;
}

2.洛谷 P4053 JSOI2007 建筑抢修

题意

nnn 个事件,做第 iii 件事要 tit_iti 的事件,只能在 edied_iedi 之前做完。问最多能做多少件事。

1≤n≤1.5×1051\le n\le 1.5\times 10^51n1.5×1051≤ti<edi<2311\le t_i<ed_i<2^{31}1ti<edi<231

思路

很经典的题目。我们先对限制 ededed 排序,尽可能做每件事,设 curcurcur 表示做完所有所选事件的结束时刻。

cur+ti>edicur+t_i>ed_icur+ti>edi,考虑用当前事件替换已选事件,并且想要 curcurcur 变小以实现全局更优。那就用小根堆维护已选事件的 ttt,若 ti<q.topt_i<q.topti<q.topcur−ti+q.top≤edicur-t_i+q.top\le ed_icurti+q.topedi,说明 tit_iti 可以替换 q.topq.topq.top 对应的事件,并且耗时减少。

满足这个条件的替换总是可行的!因为 q.topq.topq.top 之间的事件总是在限制之前,q.topq.topq.top 之后的事件全部往前推,必然在限制之内。

如果加 iii 事件,导致后面某个事件 jjj 无法加入怎么办?这应该是大多数人一开始会有的问题。实则针对这一次加入 iii,后面 jjj 同样会进行这样一次“反悔操作”。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2e5+9;
ll n;
struct node
{ll t,ed;
}a[N];
bool cmp(node x,node y)
{return x.ed<y.ed;
}
priority_queue<ll>q;
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){ll t,ed;scanf("%lld%lld",&t,&ed);a[i]=(node){t,ed};}sort(a+1,a+n+1,cmp);ll cur=0,ans=0;for(int i=1;i<=n;i++){cur+=a[i].t;if(cur<=a[i].ed){ans++;q.push(a[i].t);}else {cur-=a[i].t;if(q.empty())continue;ll tem=q.top();if(a[i].t<tem&&cur-tem+a[i].t<=a[i].ed){cur=cur-tem+a[i].t;q.pop();q.push(a[i].t);}}}printf("%lld",ans);return 0;
}

类似的题目还有洛谷 P2949、P14097:前者基本相同,后者把结束时间的限制改成了开始时间的限制而已。

3.CF725D Contest Balloons

题意

ACM比赛,AC一题会有一个气球。现在有nnn 支队伍,每支队伍的重量是 wiw_iwi ,拥有 tit_iti 个气球 ,当一支队伍的气球个数比它的重量都要大时,这个队伍就会飘起来,从而被取消比赛资格。

现在你带领的是 111 号队,你希望你队伍的名次尽可能靠前,你是个有原则的人,不会偷气球,但你可以把气球送给别的队伍,让他们飞起来。
求最终你的队伍所获得的最好名次。

2≤n≤3×1052\le n\le 3\times 10^52n3×1050≤ti≤wi≤10180\le t_i\le w_i\le 10^{18}0tiwi1018

思路

决定排名先后的是气球数量的多少。

111 对单独拎出来,对 2∼n2\sim n2n 按照气球个数 cntcntcnt 从大到小排序,因为 cnt1cnt_1cnt1 会不断送出去气球,所以会不断有队伍跑到 111 队所在名次之前。用一个指针 pospospos 维护当前哪个队伍在 111 队排名之上。

将排名在 111 队之上的队伍的 wi−ti+1w_i-t_i+1witi+1 加入优先队列,作为“作案名单”。

挑所需气球更少的队伍送给它气球即可。因为 111 队气球减少,会导致后面的队伍跑到前面去,于是不保证每一步都比上一步更优。每一次给其他队伍气球的决策,都要更新一遍答案。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3e5+9;
ll n;
struct node
{ll cnt,w;
}a[N];
bool cmp(node x,node y)
{if(x.cnt!=y.cnt)return x.cnt>y.cnt;return x.w<y.w;
}
priority_queue<ll,vector<ll>,greater<ll> >q;
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){ll cnt,w;scanf("%lld%lld",&cnt,&w);a[i]=(node){cnt,w};}sort(a+2,a+n+1,cmp);ll pos=2,ans=n;while(1){while(pos<=n&&a[1].cnt<a[pos].cnt){q.push(a[pos].w-a[pos].cnt+1);pos++;}if(q.empty()){puts("1");exit(0);}ans=min(ans,(ll)q.size()+1ll);ll tem=q.top();if(a[1].cnt<tem)break;a[1].cnt-=tem;q.pop();}printf("%lld",ans);return 0;
}

其它要动用一些小技巧的题目:CF865D(“反悔”时加入两次,方便后面的反悔,提交记录)。

4.P4597 Sequence 加强版

题意

给定一个长度为 nnn 的序列 aaa,每次操作可以把某个数 +1 或 −1。要求把序列变成非降数列。

n≤5×105n\le 5\times 10^5n5×105

还有洛谷 P2893、P4331、CF13C、CF713C,不同在于数据范围或题意。

思路

很大争议的一道题。费尽心血写 Slope Trick 的看不起码量很小的反悔贪心的,写反悔贪心的又不好好证明……不过确实难证明捏。

设当前 aia_iai 小于之前的最大数 mxmxmx(在之前已经实现单调不降,1∼i−11\sim i-11i1)。为了使得序列不降,我们要选一个 t∈[mx,ai]t\in[mx,a_i]t[mx,ai],让 mx,ai→xmx,a_i\to xmx,aix。我们发现 xxx 无论取多少,操作代价都是 mx−aimx-a_imxai

贪心地,为了使得后面的数更容易变为单调不降,我们让 mxmxmx “变为”尽量小的 aia_iai

这样做对吗?为什么能改变 mxmxmx?序列不会就不满足不降了吗?

为了使序列不降,mxmxmx 不能小于其之前的最大值 mx′mx'mx1∼i−21\sim i-21i2)。若 mx′≤aimx'\le a_imxai,那么直接更新没有问题。否则 mx′∈[ai,mx]mx'\in[a_i,mx]mx[ai,mx],那就让 ai→mx′a_i\to mx'aimxmx→mx′mx\to mx'mxmx,代价依然为 mx−aimx-a_imxai

总结:“反悔”是相当于让 mxmxmx 减得尽量小,使得后面的数更容易变为不降。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,a;
priority_queue<ll>q;
ll ans;
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){scanf("%lld",&a);q.push(a);if(a==q.top())continue;ans+=q.top()-a;q.pop();q.push(a);}printf("%lld",ans);return 0;
}

部分思路参考这篇博客。更加严谨的 Slope Trick 证明。

http://www.dtcms.com/a/478971.html

相关文章:

  • 十二、OpenCV中的边缘检测
  • 个人网站 摄影展示wordpress神箭手
  • 天津网站建设icp备微建网站
  • 软考~系统规划与管理师考试——真题篇——章节——第20章 数字乡村发展规划——纯享题目版
  • 旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
  • 网站设计需求方案wordpress转成中文版
  • 最佳经验网站网站有哪些布局
  • 爬虫的基本流程:从发送请求到数据清洗的完整链路
  • 大连手机自适应网站制作价格百度权重查询
  • 当地网站建设问卷调查建筑设计一般用什么软件
  • 淘宝商品详情 API 介绍
  • 【车机应用管理器 GUI:一款高效的 Android 车机应用与系统命令管理工具】
  • d41:MyBatisPlus入门,注解,配置,条件构造器,自定义SQL,IService
  • 沈阳网官方网站重庆男科医院哪家好
  • 无广技术贴!【PDF编辑器】Solid Converter PDF保姆级图文下载安装指南——实用推荐之PDF编辑软件
  • Fail2ban安装及配置教程:防止ECS暴力破解
  • 自做的网站如何发布松江建设新城有限公司网站
  • 住建城乡建设部网站网站建设空间主机的选择
  • 阿里美团京东从“三国杀”到“双雄会”:本地生活无限战争的终局猜想
  • wpf之Interaction.Triggers
  • 网站建设新的技术方案企业网站建设东莞
  • SSM管理系统c4ki9(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 珠海正规网站制作排名费用多少无锡阿凡达建设
  • asp网站建设 aws西安高校定制网站建设公司推荐
  • PyTorch 实现 MNIST 手写数字识别完整流程(含数据处理、模型构建与训练可视化)
  • 【Java并发编程】概念与核心问题、线程核心、并发控制、线程池、并发容器、并发问题
  • C++模板编程实战记录:SFINAE核心技术深度掌握
  • Spring Boot项目的常用依赖有哪些?
  • 保姆级教程 | ASE学习过程记录分析
  • 网站如何留言免费网站seo排名优化