在一个果园中,有 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]的时间