2020年1月21日山师训练赛1

探讨了在飞机延误情况下,如何通过贪心算法安排飞机起飞时间以最小化经济损失。文章详细解析了贪心策略的证明,介绍了使用优先队列和set实现的两种解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

比赛题目

就补了能补的

C

题意:一个机场有N架飞机,正常来说飞机从第一分钟开始起飞,且每分钟只能起飞一架,现在飞机因延误,k分钟前不能有飞机起飞,每一架飞机起飞延误都会损失钱,让你来搞一搞怎么安排飞机损失成本最小

读完题目可以想到是贪心,关于贪心的证明我是看的这个大佬的:贪心证明的原博客
附上证明:首先,很容易想到这个题目利用贪心策略,所以我们首先要确定如何来进行贪心。每架飞机每延误一分钟都造成损失,那么我们只要保证每一分钟的损失都是最小的,那么结果一定是最优的。(正确性显然)
所以我们对当前的某一分钟进行分析,都有那些飞机可能在这一分钟产生损失呢?那些晚点的飞机和当前这一分钟正点的飞机都有可能产生损失。所以我们从这些飞机中选择出来一个每分钟损失最大的飞机,让它在这一分钟起飞,那么这一分钟这一架飞机就不会产生损失(正点的和晚点的都是),并且之后也不会在产生损失。其他的飞机(包括晚点的和正点的)都会在这一分钟产生损失。这样我们每一分钟的方案就确定下来了。
那么如何来实现呢?我们可以借助于STL中的优先队列(最大堆)来实现这个功能。先晚点的都压入到优先队列中,然后每分钟压入当前正点的飞机,并弹出这一分钟应该起飞的飞机。当没有正点的飞机时,逐个弹出队列中的元素即可。
————————————————
版权声明:本文为CSDN博主「Bug_Programmer」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42165981/article/details/81094323

解法:重点是飞机前k分钟不能起飞,而且延迟后起飞的飞机不能比对应的原本就要起飞的时间还早,根据上面的那个证明,我们可以得知从第k+1分钟到第k+n分钟起飞的要从已经晚点的和正好卡点的飞机中选择,选择延迟损失大的飞即可,用优先队列和set都可以

优先队列代码:

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
struct f{
	int hou; 
	int yuan;
	int cost;
	friend bool operator <(f a1,f a2)
	{
		return a1.cost<a2.cost;
	}
}x[300010],w;
long long sum=0,n,k;
priority_queue<f>q;
int main()
{
	cin>>n>>k;
     
     for(long long i=1;i<=n;i++)
     {
     	cin>>x[i].cost;
     	x[i].yuan=i;
	 }
	 long long i=1;
	 for(;i<=k;i++)//把前k分钟的压入优先队列,这样的才是延迟飞行的 
	 {
	 	q.push(x[i]);
	 }
	 for(;i<=n+k;i++) 
	 {   if(i<=n)//!!这个条件必须写,因为最多n架,超过n之后就没飞机了 
	  {
	 	q.push(x[i]);//这是把正点的飞机压进去 
	 }
	 	w=q.top();
	 	q.pop();
	 	sum+=(i-w.yuan)*w.cost;//只有原时间小于i的还有等于i(正点)的飞机这一刻才能起飞 
	 	x[w.yuan].hou=i;//这是把对应的原时间改成延迟后的时间了,一定会把每个飞机的时间都改成延迟后的时间因为循环n次 
	 	
	 }
	cout<<sum<<endl;
	for(long long p=1;p<=n;p++)
	cout<<x[p].hou<<' ';
	
	
 } 

学set的时候学到了一个新的东西,就是set库下的函数:
lower_bound,作用:二分查找一个有序数列,返回第一个大于等于x的数的迭代器位置,如果没找到,返回末尾的迭代器位置 。
upper_bound,作用:二分查找一个有序数列,返回第一个大于x的数的迭代器位置,如果没找到,返回末尾的迭代器位置 。

set的代码:

#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
struct f{
	int yuan;
	int cost;
}x[300010],w;
long long sum=0,n,k,y[300010]={0};
bool cmp(f a,f b)
{
	if(a.cost==b.cost)
	return a.yuan>b.yuan;
	else
     return a.cost>b.cost;
}
set<int>q;
int main()
{
	cin>>n>>k;
     
     for(long long i=1;i<=n;i++)
     {
     	cin>>x[i].cost;
     	x[i].yuan=i;
     	q.insert(k+i); 
	 }
	 sort(x+1,x+1+n,cmp);
	 for(long long i=1;i<=n;i++)
	 {
	 	long long w=*q.lower_bound(x[i].yuan);
	 	   sum+=(w-x[i].yuan)*x[i].cost;
			y[x[i].yuan]=w;//这里由于x[i]在上面的快排里排序了,我们不能直接让x[i].yuan=w了,这样直接赋值对应的就不是一开始起飞的飞机了 
			q.erase(w);//应该找到原来的飞机对应序号赋上对应的延迟起飞的时间 
	 }

	cout<<sum<<endl;
	for(long long p=1;p<=n;p++)
	cout<<y[p]<<' ';
	
	
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值