码蹄集 MT2103 摘果子

在一个果园中,有 n 种果树,他们分布在一条数轴上,有独立的坐标。小码哥被奖励了许多果子,但是这些果子需要他自己去摘,小码哥摘一次果子需要 1 分钟。现在小码哥在第一颗果树下开始行走,从第 i 棵走到第 i+1 棵树需要 ti 的时间。如果小码哥选择在这棵果树下摘,那么第一个 1 分钟将会摘下 a的果子,第二次摘将摘下 ai - di个果子,第三次将摘下 ai - 2*di 个果子,以此类推。但是小码哥的时间有限,只有 t 的总时间来摘果子。
请你帮助他算出,可以摘下的最大果子数是多少?

输入格式:

第一行输入正整数 n,t ;

第二行 n 个数,表示第一次可以摘的果子数 ai;

第三行 n 个数,表示每次摘减少的可摘 di;

第四行 n - 1个数,表示树之间的间隔行走时间 ti。

输出格式:

输出一个数,最大的果子数 

输入:

2 12
10 6
2 5
2

 输出:

37 

AC代码:

//贪心+优先队列 

#include<bits/stdc++.h>
using namespace std;
int const maxn=10001;
struct TREE{
	int num,id;  //当前每棵树在规定时间内可以摘的果子数以及树的id 
	bool operator < (const TREE &x) const { return num < x.num; }
}tree[maxn];
int n,t,d[maxn],ti[maxn],ans;
priority_queue<TREE> q;
int main(){
	//输入操作
	cin>>n>>t;
	for (int i=1;i<=n;i++){
		cin>>tree[i].num;
		tree[i].id=i;
	} 
	for (int i=1;i<=n;i++) cin>>d[i];
	for (int i=1;i<n;i++) cin>>ti[i];
	
	//枚举能够走到的果树的id,一共有n棵树循环n次
	//每次循环,将第1到第i棵树的最多可以摘下的果子数存储在now里面,最终输出最大的now 
	for (int i=1;i<=n;i++){
		t-=ti[i-1];
		int now=0;
		while(!q.empty()) q.pop();   //清空队列,pop将队列中最靠前位置的元素拿掉,是没有返回值的void函数
		for(int j=1;j<=i;j++) q.push(tree[j]);
		for(int j=1;j<=t;j++){
			TREE s;
            //top是取出栈顶元素,不会删掉栈里边的元素
            //当q为priority_queue时用top,当q为queue时用front
			s=q.top();   
			if(s.num>0) now+=s.num;
			s.num-=d[s.id];
			q.pop(),q.push(s);
		}
		ans=max(ans,now);
	}
	cout<<ans;
	return 0;
} 

上面的代码算法中隐藏了一个很重要的点,一开始我没理解到它,因为我觉得很奇怪,他每次找最大果实数去摘,但它通过最优队列一会选一棵树去摘一分钟的果实,一会又选另外一棵树去摘一分钟果实,但是过程中并没有计算相应的跨越树所要消耗的时间啊?

后面我想了一会,发现它是一个很奇妙的想法:

因为树果是在每次摘取后减少的,说明是与次数有关,而不和时间有关(即你早去摘和晚去摘是同样数量),所以虽然他是通过优先队列找最大值的果实树然后去摘,并不意味着他摘了这棵树后就要跑到其他树去摘(往返摘):

比如题目要求从第一棵树出发,我们假设现在只有两棵树,第一棵树和第二棵树直接要耗费 k (k>3) 分钟(题目给出:摘果实要 1 分钟),时间为 k+3 , 树1第一次摘为a,第二次摘为a-2,树2第一次摘为a+2,第二次摘为a-3,第三次摘为a-8。得到的最大果实数标准答案是:3a

(按照我一开始的想法)此时第一次发现第二棵树的果实最大,所以跑去摘第二棵树的果实,过去消耗k,摘果实消耗1分钟,剩2分钟,得到果实数a+2,但是因为k>3,所以我没法回去,只能继续摘树2,当时间结束了,我一共能摘树1共0次,树2共3次,总共得到果实3a-9,当时我就纳闷了,这也没法通过贪心来算啊,因为摘玩第二次果实的时候,树1 能得到的果实数a-2 明显大过树2的a-3,我们应该回到树1去摘果实,但很明显回不去,得到的也不是正确答案!

后面想到根据贪心算法来看我们第一次是要去摘树2,而第二、三次摘得树都是树1,当不看顺序的时候,我们清楚的统计到,树1要摘两次,树2摘1次,同时我意识到摘得果实数与时间前后无关,只与次数有关,所以正确的操作是我们先在树1摘两次,再消耗k分钟跑到树2摘一次,从而得到最大果实数3a!

这也是为什么代码中是遍历1~n棵树,然后有个t-=ti[i-1],就像我刚刚举得例子,虽然他先选树2后再选树1,但其实从树1到树2只跑了1次,只耗费了一个ti[0]的时间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值