蓝桥杯2021年第十二届国赛真题-巧克力

题目描述

小蓝很喜欢吃巧克力,他每天都要吃一块巧克力。
一天小蓝到超市想买一些巧克力。超市的货架上有很多种巧克力,每种巧克力有自己的价格、数量和剩余的保质期天数,小蓝只吃没过保质期的巧克力,请问小蓝最少花多少钱能买到让自己吃 x 天的巧克力。

输入

输入的第一行包含两个整数 x, n,分别表示需要吃巧克力的天数和巧克力的种类数。
接下来 n 行描述货架上的巧克力,其中第 i 行包含三个整数 ai, bi, ci,表示第 i 种巧克力的单价为 ai,保质期还剩 bi 天(从现在开始的 bi 天可以吃),数量为 ci。

输出

输出一个整数表示小蓝的最小花费。如果不存在让小蓝吃 x 天的购买方案,输出ࠪ 1。

样例输入复制

10 3
1 6 5
2 7 3
3 10 10

样例输出复制

18

提示

【样例说明】
一种最佳的方案是第 1 种买 5 块,第 2 种买 2 块,第 3 种买 3 块。前 5 天
吃第 1 种,第 6、7 天吃第 2 种,第 8 至 10 天吃第 3 种。
【评测用例规模与约定】
对于 30% 的评测用例,n, x ≤ 1000。
对于所有评测用例,1 ≤ n, x ≤ 100000,1 ≤ ai, bi, ci ≤ 10^9。

解题思路:这个题给我的感觉有点像贪心,难点就在于如何选择贪心策略,如果我们按照正常思路,从第一天开始就选择能吃的巧克力,前面几天选择的范围比较大,到了后面几天就相对比较难选了,而且前面的选择会直接影响最后是否会有解以及解的大小,但是前面在选的时候我们根本无法对其造成的影响进行估计,举个例子:如果从前面开始选,一开始我们会面临很多种选择,我们通常会选择最优策略,也就是说保质期能满足当前这一天,并且价格最便宜,但如果存在这样一种巧克力,保质期最长,价格也是最低的,我们在一开始就把它给用了,到了后面就不能用了,也就是说它原本可以在很多天之后吃就行,现在却在前几天就吃了,吃的时候保质期还有很长的一段事件,到了后面可供选择的巧克力保质期就会越来越短,很可能会出现得不到一个可行方案的情况,对于这种情况,其实我们把前后选择的巧克力对调一下就能解决,把前面选的保质期长,价格低的巧克力放在后面吃,把后面价格高,保质期短的放在前面吃,通过这样一通分析,我们可以惊奇地发现,正着来不行,我们从后面来貌似就比较合理,这也就引申出了我们的正确的策略:

将所有种类的巧克力按保质期从长到短排序,然后我们从后面几天开始选择吃哪块巧克力,对于第i天,我们将所有保质期满足能在第i天吃的巧克力都找出来,然后对它们按价格从小到大排序,选择价格最低的在第i天吃,并记录一下这种巧克力已经用掉了几块,如果全部都用掉了,那就不能再用了,这里我们用到了优先队列来动态维护当天能吃的各种巧克力,用map来维护不同种类的巧克力已经用掉了几块。

上代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue> 
#include <map>
using namespace std;
const int N=1e5+10;
struct node{
	long long a;
	long long b;
	long long c;
	int id;
}goods[N];
int x,n; 
map<int,int>mp;
bool cmp(struct node x1,struct node x2)
{
	return x1.b>x2.b;
}
struct cmp1{
	bool operator()(const node &p,const node &q)
	{
		return p.a>q.a;//从小到大排序 
	}
};
int main()
{
	cin>>x>>n;
	for(int i=1;i<=n;i++)
	cin>>goods[i].a>>goods[i].b>>goods[i].c,goods[i].id=i;
	sort(goods+1,goods+n+1,cmp);//按保质期从大到小排序 
	long long ans=0;
	priority_queue<node,vector<node>,cmp1>q;
    int j=1;
	for(int i=x;i>=1;i--)
	{
		while(goods[j].b>=i&&j<=n)//把所有能用的巧克力都找出来放到优先队列中 
		{
			q.push(goods[j]);
			j++;
		}
		if(!q.size())//如果中间某一天没有可以选择的巧克力,那就不存在一个合法的方案 
		{
			cout<<"-1"<<endl;
			return 0; 
		}
		node temp=q.top();
		mp[temp.id]++;
		ans+=temp.a;
		if(temp.c==mp[temp.id])//当前种类的巧克力已经全部用掉了,以后不能再用 
		q.pop();
	}
	cout<<ans<<endl;
	return 0;
}

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值