贪心好题(经典模型题)

贪心

贪心问题往往难在思路的推理和应用,而代码是比较的短的,因此对于此类题目需要进行多次的训练来锻炼自身的思考能力,也要会记录一些好题目来回顾。

P2751 工序安排

题目要求解决工件在两种工序(操作 A 和操作 B)中的最优机器分配问题,以最小化各工序的最大完成时间。具体来说:

  1. 操作 A 分配:将 N 个工件分配到 M1 台 A 型机器上,每台机器处理工件的时间已知。需使所有工件完成操作 A 的最晚时间(即各机器处理工件的累计时间的最大值)最小。
  2. 操作 B 分配:在操作 A 完成的基础上,将 N 个工件分配到 M2 台 B 型机器上,每台机器处理工件的时间已知。每个工件的操作 B 必须在其操作 A 完成后开始,需使所有工件完成操作 B 的最晚时间(即各机器处理工件的累计时间与对应操作 A 完成时间的最大值之和的最大值)最小。

最终需输出操作 A 和操作 B 的最小最大完成时间。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 1010
int a[MAXN],b[MAXN],t[MAXN];
struct node{
	int s,v;
};
bool operator <(node x,node y){
	return x.s>y.s; 
}
priority_queue<node> q;
signed main(){
	ios;
	int n,n1,n2;
	cin>>n>>n1>>n2;
	for(int i=1;i<=n1;i++)cin>>a[i];
	for(int i=1;i<=n1;i++){
		q.push({a[i],a[i]});
	}
	for(int i=1;i<=n;i++){
		t[i]=q.top().s;
		int y=q.top().v;
		q.pop();
		q.push({t[i]+y,y});
	}
	while(q.size())q.pop();
	cout<<t[n]<<" ";
	for(int i=1;i<=n2;i++){
		cin>>b[i];
		q.push({b[i],b[i]});
	}
	int mx=0;
	for(int i=n;i>=1;i--){
		auto x=q.top();
		q.pop();
		mx=max(mx,t[i]+x.s);
		x.s+=x.v;
		q.push(x); 
	}
	cout<<mx;
	return 0;
}

通过最小根堆维护,每次取出最小的元素并将时间更新,最后t为n时的时间即为完成A任务所需要的最小时间,完成B任务时可采取反向最小根堆,从后往前依次进行数据的更新

P3076 [USACO13FEB] Taxi G

描述:长度为m的栅栏上,有n头牛需要坐车前往别的地方,起点和终点分别为a_i和b_i。现在一辆出租车从最左端0出发,要运送完所有牛,最后到达最右端m,求最小路程。出租车只能一次载一只牛。

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 100010
#define int long long
int s[MAXN],t[MAXN];
signed main(){
	ios;
	int n,m;
	cin>>n>>m;
	int ans=0;
	for(int i=1;i<=n;i++){
		cin>>s[i]>>t[i];
		ans+=abs(s[i]-t[i]);
	}
	n++;
	s[n]=m,t[n]=0;
	sort(s+1,s+1+n);
	sort(t+1,t+1+n);
	for(int i=1;i<=n;i++)ans+=abs(s[i]-t[i]);
	cout<<ans;
	return 0;
}

贪心思路:易知,对于一段有起点和终点的路程,该路程是不能省略的,则需要考虑如何减少司机接单的空载时间,将起点和终点排序,并将0和终点m加入到数组中,易知终点到对应序列的起点的间隔的路程差最短,因为后面的终点肯定比当前的终点更远,现在不用对应起点,后面再用可能不亏但一定不赚,因此贪心思路证明完成

P1248 加工生产调度

​ 使用Johnson算法,Johnson 规则用于解决两台机器的流水作业调度问题,其中每个作业必须先在第一台机器上加工,然后再在第二台机器上加工。在这个问题中,A 车间相当于第一台机器,B 车间相当于第二台机器,产品的 A 时间 a[ i ] 和 B 时间 b[ i ] 分别对应在两台机器上的加工时间。

例题:P1248 加工生产调度

解法:对于产品加工时间a[i]<b[i]的情况,采取a[i]非递减的方式排序,对于 a [ i ] > = b [ i ] a[i]>=b[i] a[i]>=b[i] 的情况,采取 b [ i ] b[i] b[i] 非递减的方式排序

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define int long long
#define MAXN 10010
struct node{
	int a,b,c;
};
int a[MAXN],b[MAXN];
bool cmp1(node x,node y){
	return x.a<y.a;
}
bool cmp2(node x,node y){
	return x.b>y.b;
}
void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	vector<node> q,p;
	for(int i=1;i<=n;i++)
	if(a[i]<=b[i])q.push_back({a[i],b[i],i});
	else p.push_back({a[i],b[i],i});
	sort(q.begin(),q.end(),cmp1);
	sort(p.begin(),p.end(),cmp2);
	int ans=0;
	int cn1=0,cn2=0;
	for(int i=0;i<q.size();i++){
		cn1+=q[i].a;
		if(cn1>cn2)cn2=cn1;
		cn2+=q[i].b;
	}
	for(int i=0;i<p.size();i++){
		cn1+=p[i].a;
		if(cn1>cn2)cn2=cn1;
		cn2+=p[i].b;
	}
	cout<<cn2<<endl;
	for(int i=0;i<q.size();i++)
	cout<<q[i].c<<" ";
	for(int i=0;i<p.size();i++)
	cout<<p[i].c<<" ";
}
signed main(){
	ios;
	int t=1;
	//cin>>t;
	while(t--)
	solve();
	return 0;
}

CF549G Happy Line

给定一个 n 个元素的整数序列 a, 任意时刻对于任一对相邻元素 (a[i-1],a[i]),若a[i-1] > a[i]则要依次执行如下两个操作:

a[i-1]--,a[i]++,然后交换 a[i-1] 和 a[i] 的位置。

经过若干次 1、2 操作后,若能使整个序列变成非降的,则输出最终的序列;否则输出 :(

1≤n≤2×105,0≤a[i]≤109。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 1000010
int a[MAXN],b[MAXN];
signed main(){
	ios;
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)b[i]=a[i]+i;
	sort(b+1,b+1+n);
	for(int i=1;i<=n;i++)
	if(b[i]==b[i-1]){
		cout<<":(";
		return 0;
	}
	for(int i=1;i<=n;i++)
	cout<<b[i]-i<<" ";
	
	return 0;
} 

解法:可以知道,每次交换相邻元素时,前一个元素值-1,下标+1,后一个元素值-1,下标+1,可以知道其元素值加下标的总数是不会变的,可以弄一个b数组记入a[i]+i的值,再按从小到大的次序排序,当有两个b数组的值相同时,不存在一个排列能满足要求,否则,输出b[i]-i的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

convie05

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值