前言
反悔贪心,先选择局部最优,当不断向后更新时发现前面的决策在现在来看并不优就进行调整过去决策进行局部最优,可以说反悔贪心一步步扩宽自己的视野从而明白过去的错误。、
如果动态规划是将各种削苹果的方式都展示出来的话,那反悔贪心就是削一下补回来一点再削。
当然反悔贪心的题是可以用动态规划做的,但是在有些题数据范围很大或不方便转移时就可以考虑反悔贪心。
反悔堆
用堆维护最差的决策,之后有更优的决策可以快速掉替换这个决策。
P2949 Work Scheduling G
因为有个截至时间,所以我们贪心地想截至时间越晚的我们越靠后考虑,当然我们也想让价值更大,所以我们按照截至时间从小到大排序,其次按照权值排序。
然后我们用小根堆维护选择的数,如果我们新加的数的截至时间为堆内元素个数,那么我们就要看看能不能删掉数,我们查看堆顶元素是否小于新加的元素,如果小于我们就删掉堆顶让当前元素入堆,我们不断地进行贪心与策略调整。
远古时期的代码,和我讲的有一点点点区别。
#include <bits/stdc++.h>
using namespace std;
int n;
long long ans=0;
struct ss{
int t,p;
}a[100005];
bool cmp(ss g,ss h){
return g.t<h.t;
}
priority_queue<int,vector<int>,greater<int> >q;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].t,&a[i].p);
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
if(a[i].t<=q.size()){
if(q.top()<a[i].p){
ans-=q.top();
q.pop();
q.push(a[i].p);
ans+=a[i].p;
}
}
else{
ans+=a[i].p;
q.push(a[i].p);
}
}
cout<<ans;
return 0;
}
P4053 [JSOI2007] 建筑抢修
通过不断调整使得花费总时间尽量少,改一改上面那题的条件与不断调整 \(sum\) 值。
反悔自动机
通过特殊的策略使得总是维护最优策略,通常应用到有特殊限制的题目上,例如 \(A\) 和 \(B\) 两者选 \(1\),\(C\) 和 \(D\) 两者选 \(1\),假设我们已选 \(A,C\),我们想选 \(B\) 的话就要加上 \(B-A\) 使得自动反悔 \(A\) 从而维护最优策略。
Buy Low Sell High
我们考虑我们答案形式并进行转变为 \(C_{sell}-C_{buy}\) 我们通过售价减购价的形式,我们考虑引入一个反悔物使得我们每当销售例如 \(C_i-C_j\) 就会产生这个反悔物 \(val\),当我们之后选择这个反悔物时就可以更新为新的获利 \(C_i-C_k\),于是我们有以下方程 \(C_i-C_j+C_k-val=C_i-C_k\),最后可知 \(C_j=val\),所以我们用小根堆维护最小的数,如果最小的数一个新的数那也就是产生新的组合,总的加多少减多少是不变的,反正不亏,如果最小的数是反悔物那么我们也进行反悔策略,调整变得更大,反正不亏。
#include <bits/stdc++.h>
#define int long long
#define re register
using namespace std;
int n;
int ans;
priority_queue<int,vector<int>,greater<int> > q;
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(!q.empty()&&q.top()<x){
ans+=x-q.top();
q.pop();
q.push(x);
}
q.push(x);
}
cout<<ans;
return 0;
}
标签:val,int,long,反悔,算法,我们,贪心
From: https://www.cnblogs.com/sadlin/p/18531055