2021.11.04 反悔贪心

反悔贪心 - nth_element - 博客园 (cnblogs.com)

【学习笔记】反悔贪心 - RioTian (cnblogs.com)

进阶贪心--带后悔的贪心(持续更新)_..-CSDN博客_反悔贪心

1.反悔贪心

贪心本身是没有反悔操作的,贪心求的就是当前的最优解。但当前的最优解有可能是局部最优解,而不是全局最优解,这时候就要进行反悔操作。

2.分类

  1. 反悔自动机:

    即设计一种反悔策略,使得随便一种贪心策略都可以得到正解。

    基本的设计思路是:每次选择直观上最接近全局最优解的贪心策略,若发现最优解不对,就想办法自动支持反悔策略。(这就是自动机的意思)

    具体题目具体分析。一般需要反悔自动机的题都是通过差值巧妙达到反悔的目的。

  2. 反悔堆:

    即通过(大根堆、小根堆)来维护当前贪心策略的最优解,若发现最优解不对,就退回上一步,更新最优解。

    由于堆的性质,使得堆的首数据一定是最优的,这就可以实现快速更新最优解

2.1 反悔堆

时间一定

[P2949 USACO09OPEN]Work Scheduling G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

#define int long long
const int N=1e6+10;
int n,ans;
struct node{
	int time,val;
	bool operator <(const node &b)const{
		return time<b.time;
	}
}a[N];
priority_queue<int,vector<int>,greater<int> >q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

signed main(){
	n=read();
	for(int i=1;i<=n;i++)a[i].time=read(),a[i].val=read();
	sort(a+1,a+n+1);
	for(int i=1;i<=n&&a[i].time<=1e9;i++){
		if(a[i].time<=q.size()){
			if(a[i].val>q.top()){
				ans-=q.top();q.pop();
				ans+=a[i].val;q.push(a[i].val);
			}
		}else{
			ans+=a[i].val;q.push(a[i].val);
		}
	}
	cout<<ans;
	return 0;
}

价值一定

[P4053 JSOI2007]建筑抢修 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=150010;
int n,ans,maxn;
struct node{
	int timei,endi;
	bool operator <(const node &b)const{
		return endi<b.endi;
	}
}a[N];
struct nodei{
	int timei,endi;
	bool operator <(const nodei &b)const{
		return timei<b.timei;
	}
};
priority_queue<nodei>q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i].timei=read(),a[i].endi=read();
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		if(maxn+a[i].timei>a[i].endi){
			if(a[i].timei>=q.top().timei)continue;
			maxn-=q.top().timei;ans-=1;q.pop();
			maxn+=a[i].timei;ans+=1;q.push({a[i].timei,a[i].endi});
		}else{
			maxn+=a[i].timei;ans+=1;q.push({a[i].timei,a[i].endi});
		}
	}
	cout<<ans;
	return 0;
}
/*
[Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11
*/

2.2 反悔自动机

1.双向链表

[P1792 国家集训队]种树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

// https://www.luogu.com.cn/blog/zhouzhuo/solution-p1792
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=2e5+10;
int n,m,val[N],L[N],R[N],vis[N],ans;
struct node{
	int id,val;
	bool operator <(const node &b)const{
		return val<b.val;
	}
};
priority_queue<node>q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline dlt(int x){
	L[R[x]]=L[x];R[L[x]]=R[x];vis[x]=1;
}

int main(){
	n=read();m=read();
	if(m*2>n)return cout<<"Error!",0;
	for(int i=1;i<=n;i++){
		val[i]=read();
		L[i]=i-1;R[i]=i+1;
		q.push({i,val[i]});
	}
	L[1]=n;R[n]=1;
	for(int i=1;i<=m;i++){
		while(vis[q.top().id])q.pop();
		int x=q.top().id;q.pop();
		ans+=val[x];
		val[x]=val[L[x]]+val[R[x]]-val[x];
		dlt(L[x]);dlt(R[x]);
		q.push({x,val[x]});
	}
	cout<<ans;
	return 0;
}

P1484 种树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

注意:这里不是个环 ~

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=5e5+10;
typedef long long ll;
int n,m,val[N],L[N],R[N],vis[N];
ll ans;
struct node{
	int id,val;
	bool operator <(const node &b)const{
		return val<b.val;
	}
};
priority_queue<node>q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline void dlt(int x){
	R[L[x]]=R[x];L[R[x]]=L[x];vis[x]=1;
}

int main(){
	//freopen("P1484_7.in","r",stdin);
	n=read();m=read();
	for(int i=1;i<=n;i++){
		val[i]=read();
		L[i]=i-1;R[i]=i+1;
		q.push({i,val[i]});
	}
	L[1]=0;R[n]=0;
	for(int i=1;i<=min(m,(n+1)/2);i++){
		while(vis[q.top().id])q.pop();
		if(q.top().val<=0)break;
		int x=q.top().id;q.pop();
		ans+=1ll*val[x];
		val[x]=val[L[x]]+val[R[x]]-val[x];
		if(L[x])dlt(L[x]);
		if(R[x])dlt(R[x]);
		q.push({x,val[x]});
	}
	cout<<ans;
	return 0;
}

[P3620 APIO/CTSC 2007] 数据备份 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

注意:这里的val[0]与val[n]是正无穷,链表的上下界处理,我们要保证表头和表尾不被选择(它们不包含在任一子问题中),只需把初值赋为无穷大即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

#define int long long
const int N=1e6+10;
typedef long long ll;
int n,m,val[N],L[N],R[N],vis[N];
ll ans;
struct node{
	int id,val;
	bool operator <(const node &b)const{
		return val>b.val;
	}
};
priority_queue<node>q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline void dlt(int x){
	L[R[x]]=L[x];R[L[x]]=R[x];vis[x]=1;
}

signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)val[i]=read();
	//sort(val+1,val+n+1);
	for(int i=1;i<n;i++){
		val[i]=val[i+1]-val[i];
		L[i]=i-1;R[i]=i+1;
		q.push({i,val[i]});
	}
	val[0]=val[n]=0x3f3f3f3f;
	L[1]=R[n-1]=0;
	for(int i=1;i<=m;i++){
		while(vis[q.top().id])q.pop();
		int x=q.top().id;q.pop();
		ans+=1ll*val[x];
		val[x]=val[L[x]]+val[R[x]]-val[x];
		if(L[x])dlt(L[x]);
		if(R[x])dlt(R[x]);
		q.push({x,val[x]});
	}
	cout<<ans;
	return 0;
}

类似题:

CF958E2 Guard Duty (medium) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

2.堆

CF865D Buy Low Sell High - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

我们将每天的价格视为一个个"选项", 压入小根堆中,为了保证买入操作在卖出操作之前,我们从前往后扫描pp,对于现在的价格p_i,如果堆顶元素p_j 满足 p_j<p_i ,那么,我们取出堆顶,在第jj天买入股票,在第ii天卖出股票,此时,我们就可以获得p_i - p_j的收益

然而,如果之后有p_k满足p_k > p_i,辣么,我们当前作出的决策可能并不是最优的,如何反悔呢?

于是,当我们进行上述操作时,我们将p_i也压入堆中,增加一个p_i的选项,弹出时,我们相当于将p_j按照p_i的价格又买了回来.

注:必须把!q.empty()放前面,不然会莫名挂掉!!!

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

const int N=3e5+10;
typedef long long ll;
int n;
ll ans;
priority_queue<int,vector<int>,greater<int> >q;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

int main(){
	n=read();
	for(int i=1;i<=n;i++){
		int x;//x=read();
		cin>>x;
		//cout<<"Case 1 "<<i<<" "<<x<<endl;
		if(!q.empty()&&(x>q.top())){
			ans+=(x-q.top());
			//cout<<"    Case 3"<<endl;
			q.pop();
			q.push(x);
			//cout<<"    Case 2"<<endl;
		}
		q.push(x);
	}
	cout<<ans;
	return 0;
}

思想类似题

P2107 小Z的AK计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

注:why?????为毛我的想法只有10分????!

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;

#define int long long
const int N=1e6+10;
typedef long long ll;
int n,m,ans,now,last;
struct node{
	ll x,t;
	bool operator <(const node &b)const{
		return x<b.x;
	}
}a[N];
priority_queue<int>q;

inline ll read(){
	ll s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}

signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)a[i].x=read(),a[i].t=read();
	sort(a+1,a+n+1);
	int ansi=0;
	for(int i=1;i<=n;i++){
		int xi=a[i].x-last;
		last=a[i].x;
		//cout<<i<<" "<<xi<<endl;
		/*if(xi+a[i].t+now<=m){
			q.push(a[i].t);
			++ans;now+=xi+a[i].t;
		}else{
			if(!q.empty()&&q.top()>=xi+a[i].t){
				--ans;now-=q.top();q.pop();
				++ans;now+=xi+a[i].t;q.push(a[i].t);
			}
		}*/
		++ansi;now+=xi+a[i].t;q.push(a[i].t);
		while(!q.empty()&&now>m){
			--ansi;now-=q.top();q.pop();
		}
		if(now>m)break;
		ans=max(ans,ansi);
	}
	cout<<ans;
	return 0;
}
 posted on 2021-11-04 21:39  eleveni  阅读(80)  评论(0)    收藏  举报