首页 > 编程语言 >【反悔贪心】算法讲解

【反悔贪心】算法讲解

时间:2024-06-03 10:33:27浏览次数:16  
标签:int time long t1 反悔 讲解 贪心

目录

cf865D

 环形喂猪

建筑抢修


        

        

cf865D

思路:

我们贪心的原则是尽可能的多卖,而且尽可能的卖的多。

整体的贪心思路就是能卖就卖,卖完后放入堆中(以便反悔),先不考虑能卖多少,因为堆是按照价格从小到大排序,把最优的选择放在最前面。

在遍历到第i天时候,如果我们当前卖是可以获利的,那就直接卖掉,然后把价格放入反悔堆。

如果后面还有更大的pk,那么我们可以在pi处撤销丢弃操作(也就是把pi买下来),然后在pk处丢弃。操作就是再加上pk-pi即可。

贪心就是:能卖,我们就卖,不过卖完后要把此点加入堆中,以便我们可以反悔。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll ans;
priority_queue<int,vector<int>,greater<int> >heap;//小根堆
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int p;cin>>p;
		if(heap.size()&&heap.top()<p){
			ans+=p-heap.top();
			heap.pop();
			heap.push(p);
		}
		heap.push(p);
	}
	cout<<ans;
}

        

        

 环形喂猪

思路:

 一道非常典型的反悔贪心,反悔贪心就是我们需要建立一个贪心堆,先按照正常的思路(局部最优)进行贪心,如果当前取出的不是最优解,那就反悔。

我们把所有的猪放入堆中,取出一个最大猪,然后标记左右节点,并把这三个节点删掉,但是又要考虑到方便反悔,而反悔操作应该是a[i-1]+a[i+1],而我们现在已经取了a[i],所以应该把a[i-1]+a[i+1]-a[i]直接加入堆中,并创建新的节点。以方便反悔。

#include <bits/stdc++.h>
using namespace std;
const int N=4e+5;
bool mark[N];
int n,m,l[N],r[N],ans,a[N],tot;
struct node{
	int id,v;
};
priority_queue<node>q;
bool operator<(const node &x,const node &y){
	return x.v<y.v;
}
int main(){
	cin>>n>>m;
	if(m>n/2){
		cout<<"Error!";return 0;
	}
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=2;i<n;i++){
		l[i]=i-1,r[i]=i+1; //创建环形链表
		q.push({i,a[i]});//加入大根堆
	}
	l[1]=n;r[1]=2;l[n]=n-1;r[n]=1;//起点和尾点
	q.push({1,a[1]});q.push({n,a[n]});
	tot=n;
	for(int i=1;i<=m;i++){
		node cur;cur=q.top();q.pop();
		if(mark[cur.id]){i--;continue;}//已经被删掉的点就跳过,注意i--,别又浪费了一袋
		ans+=cur.v;//更新答案
		a[++tot]=a[l[cur.id]]+a[r[cur.id]]-a[cur.id];//创建反悔点
		q.push({tot,a[tot]});//反悔点入堆
		l[tot]=l[l[cur.id]];r[tot]=r[r[cur.id]];//更新新节点的左右关系
		r[l[l[cur.id]]]=tot;l[r[r[cur.id]]]=tot;//新节点的左右节点的指向新节点
		mark[cur.id]=mark[l[cur.id]]=mark[r[cur.id]]=1;//删掉3个点
	}
	cout<<ans;
	return 0;
}

        

        

建筑抢修

思路:

我们贪心的原则是先去修那些代价比较小且很容易报废的建筑(t2比较大的建筑),

如何去修容易报废的建筑呢?那就是按照t2从大到小进行排序进去“抢修”。

所以最开始,我们先按照能修就修的原则去修每一个建筑,然后把修的建筑放入堆中(以便反悔),堆是按照代价排序,以便把最坏的决定放到堆头。

如果当前的建筑可以抢修,那么就直接修,修后放入反悔堆。

如果当前的建筑来不及抢修,我们就去“反思自己”,看看已经修的建筑中有没有代价比这个建筑大的,如果有,那么就反悔time-=heap.top().t1; time+=a[i].t1;之前的出去,新的进来。如果没有,那就直接不用反悔。

#include <bits/stdc++.h> //p4053建筑抢修
using namespace std;
const int N=150005;
int n;
struct node{
	long long t1,t2;
}a[N];
bool operator<(const node &p,const node &q){
	return p.t1<q.t1;
}
bool cmp (const node p,const node q){
	return p.t2<q.t2;
}
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		long long t1,t2;cin>>t1>>t2;
		a[i]=(node){t1,t2};
	}
	sort(a,a+n,cmp);
	priority_queue<node>heap;//大根堆
	long long time=0;//time里面是已经建好所耗费的时间,建的东西在堆中
	for(int i=0;i<n;i++){
		if(a[i].t2>=time+a[i].t1){//正常贪心(能建就去建)
			time+=a[i].t1;
		    heap.push(a[i]);	
		}
		else{
			if(a[i].t1<heap.top().t1){//(反悔贪心)我们之间建了一个很耗时间的房子,现在应该把它替换掉
				time-=heap.top().t1;
				heap.pop();
				time+=a[i].t1;
				heap.push(a[i]);
			}
	}
	}
	cout<<heap.size();
}

        

        

标签:int,time,long,t1,反悔,讲解,贪心
From: https://blog.csdn.net/m0_69478376/article/details/139378698

相关文章