反悔贪心

一.什么是反悔贪心

顾名思义,反悔贪心就是两个操作:“反悔” + “贪心”

一般来说,贪心仅能解出局部最优解。

那么在要求全局最优解时,我们就可以利用“反悔”这个操作解决。

反悔贪心的思想是:每次都进行操作,在以后有最优情况的时候再取消这次操作。

二.反悔贪心基本操作

一般来说,我们采用一个堆来存之前的贪心决策,堆顶是最劣决策。(想要让决策最优必然要撤销之前的最劣决策)

分两种情况:

  1. 符合某种条件时,推入堆,计算答案。

  2. 不符合某种条件时,与堆顶比较,做出操作。

接下来用一些例题来帮助大家理解这个算法。

三.反悔贪心例题

P3049 [USACO12MAR]Landscaping S

还有他的加强版P2748

可以算是比较板子的反悔贪心的题了。

保证 0Ai,Bi10A

有了这句话我们就可以一盆一盆来转移泥土

我们遍历每一个节点,更新 ans,ans表示到当前节点为止前面所有节点都满足要求的最小代价。

这样我们遍历完最后一个节点后,ans便是最终答案。

对于泥土不够的,我们先花费x代价添加泥土,对于泥土过剩的,我们先花费y代价移走泥土。

同时我们要维护两个堆 q1,q2

q1 存之前的添加泥土的操作

q2 存之前的移走泥土的操作

这样我们存下这些操作便于以后撤销。

堆的比较规则我们会在下面提到。

我们考虑两个花坛之间转移的情况。

情况一:当前花坛的泥土不够

我们设当前看到第 i 个花坛,并且第 i 个花坛泥土不够,我们除了选择补全泥土以外,还可以选择从之前泥土过剩的花坛中转移,所以我们要比较 j 花坛转移泥土至 i 花坛移走 j 花坛泥土且补全 i 花坛泥土 两种决策哪个代价更小,我们要选取其中的代价小的作为最优决策。

我们来考虑转移的代价

因为我们是从之前泥土过剩的花坛中转移

所以有 j<i

因此

j 花坛转移泥土至 i 花坛花费代价: z(ij)

移走 j 花坛泥土且补全 i 花坛泥土花费代价:x+y

因为我们 ans 的定义是前面所有节点都满足要求的最小代价,也就是说,此时 ans 的值是已经移走了 j 花坛泥土的代价,所以这两种决策对 ans 的贡献分别为:

j 花坛转移泥土至 i 花坛对ans的贡献: z(ij)y=zi(zj+y)

移走 j 花坛泥土且补全 i 花坛泥土对 ans 的贡献:x

不难发现,zi 是定值,想要让转移的代价最小,我们要找到一个位置 j 满足:

  1. 该位置进行过移走泥土的操作

  2. zj+y 最大

堆 q2 存的是之前的移走泥土的操作,所以 q2 内的位置均满足性质一,而我们只需让 q2 维护为一个使 zj+y 最大的大顶堆,这样我们每次取堆顶就是我们需要的位置 j

接下来更新 ans :

  • 转移的代价更大,即 zi(zj+y)>=x

    ans+=x,并将当前操作加入 q1

  • 转移的代价更小,即 zi(zj+y)<x

    ans+=z * i - (z * j + y),并取出 q2 的堆顶,并将当前操作加入 q1(有可能 ij 转移并非最优,所以要把这次也加入 q1,方便后面撤销)

情况二:当前花坛的泥土过剩

与情况一同理

设当前看到第 i 个花坛,并且第 i 个花坛泥土过剩,我们除了选择移走泥土以外,还可以选择转移到之前泥土不够的花坛中,所以我们要比较 i 花坛转移泥土至 j 花坛移走 i 花坛泥土且补全 j 花坛泥土 两种决策哪个代价更小,我们要选取其中的代价小的作为最优决策。

我们来考虑转移的代价

因为我们是转移到之前泥土不够的花坛中

所以有 j<i

因此

i 花坛转移泥土至 j 花坛花费代价: z(ij)

移走 i 花坛泥土且补全 j 花坛泥土花费代价:x+y

因为我们 ans 的定义是前面所有节点都满足要求的最小代价,也就是说,此时 ans 的值是已经补全了 j 花坛泥土的代价,所以这两种决策对 ans 的贡献分别为:

i 花坛转移泥土至 j 花坛对ans的贡献: z(ij)x=zi(zj+x)

移走 i 花坛泥土且补全 j 花坛泥土对 ans 的贡献:y

不难发现,zi 是定值,想要让转移的代价最小,我们要找到一个位置 j 满足:

  1. 该位置进行过补全泥土的操作

  2. zj+x 最大

堆 q1 存的是之前的补全泥土的操作,所以 q1 内的位置均满足性质一,而我们只需让 q1 维护为一个使 zj+x 最大的大顶堆,这样我们每次取堆顶就是我们需要的位置 j

接下来更新 ans :

  • 转移的代价更大,即 zi(zj+x)>=y

    ans+=y,并将当前操作加入 q2

  • 转移的代价更小,即 zi(zj+x)<y

    ans+=z * i - (z * j + x),并取出 q1 的堆顶,并将当前操作加入 q2(有可能 ij 转移并非最优,所以要把这次也加入 q2,方便后面撤销)

至此我们这道题的思路就讲完了。

code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=100010; 
int n,a[N],b[N],ans,x,y,z; 
priority_queue<int> q2,q1;
signed main() {
	cin>>n>>x>>y>>z;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];
		if(a[i]<b[i]){//情况一:当前花坛的泥土不够
			for(int j=1;j<=b[i]-a[i];j++)//一盆一盆转移 
				if(q2.empty()||z*i-q2.top()>x){
					ans+=x;
					q1.push(z*i+x);
				}else {
					int g=q2.top();
					ans+=z*i-g;
					q2.pop();
					q1.push(z*i+z*i-g);
				}
		}else if(a[i]>b[i]){//情况二:当前花坛的泥土过剩
			for(int j=1;j<=a[i]-b[i];j++)//一盆一盆转移
				if(q1.empty()||z*i-q1.top()>y){
					ans+=y;
					q2.push(z*i+y);
				}else {
					int g=q1.top();
					ans+=z*i-g;
					q1.pop();
					q2.push(z*i+z*i-g);
				}
		}
	}
	cout<<ans;
	return 0;
}

posted @   「ycw123」  阅读(746)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示