2021.11.04 反悔贪心
反悔贪心 - nth_element - 博客园 (cnblogs.com)
【学习笔记】反悔贪心 - RioTian (cnblogs.com)
进阶贪心--带后悔的贪心(持续更新)_..-CSDN博客_反悔贪心
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
浙公网安备 33010602011771号