Fishing Master(贪心+思维)
Problem Description
Heard that eom is a fishing MASTER, you want to acknowledge him as your mentor. As everybody knows, if you want to be a MASTER’s apprentice, you should pass the trial. So when you find fishing MASTER eom, the trial is as follow:
There are n n n fish in the pool. For the i − t h i - th i−th fish, it takes at least t i t_i ti minutes to stew(overcook is acceptable). To simplify this problem, the time spent catching a fish is k k k minutes. You can catch fish one at a time and because there is only one pot, only one fish can be stewed in the pot at a time. While you are catching a fish, you can not put a raw fish you have caught into the pot, that means if you begin to catch a fish, you can’t stop until after k k k minutes; when you are not catching fish, you can take a cooked fish (stewed for no less than t i t_i ti) out of the pot or put a raw fish into the pot, these two operations take no time. Note that if the fish stewed in the pot is not stewed for enough time, you cannot take it out, but you can go to catch another fish or just wait for a while doing nothing until it is sufficiently stewed.
Now eom wants you to catch and stew all the fish as soon as possible (you definitely know that a fish can be eaten only after sufficiently stewed), so that he can have a satisfying meal. If you can complete that in the shortest possible time, eom will accept you as his apprentice and say “I am done! I am full!”. If you can’t, eom will not accept you and say “You are done! You are fool!”.
So what’s the shortest time to pass the trial if you arrange the time optimally?
Input
The first line of input consists of a single integer T ( 1 ≤ T ≤ 20 ) T(1≤T≤20) T(1≤T≤20), denoting the number of test cases.
For each test case, the first line contains two integers n ( 1 ≤ n ≤ 1 0 5 ) , k ( 1 ≤ k ≤ 1 0 9 ) n(1≤n≤10^5),k(1≤k≤10^9) n(1≤n≤105),k(1≤k≤109), denoting the number of fish in the pool and the time needed to catch a fish.
the second line contains n integers, t 1 , t 2 , … , t n ( 1 ≤ t i ≤ 1 0 9 ) t_1,t_2,…,t_n(1≤t_i≤10^9) t1,t2,…,tn(1≤ti≤109) ,denoting the least time needed to cook the i − t h i - th i−th fish.
Output
For each test case, print a single integer in one line, denoting the shortest time to pass the trial.
Sample Input
2
3 5
5 5 8
2 4
3 3
Sample Output
23
11
Hint
Case 1: Catch the 3rd fish (5 mins), put the 3rd fish in, catch the 1st fish (5 mins), wait (3 mins),
take the 3rd fish out, put the 1st fish in, catch the 2nd fish(5 mins),
take the 1st fish out, put the 2nd fish in, wait (5 mins), take the 2nd fish out.
Case 2: Catch the 1st fish (4 mins), put the 1st fish in, catch the 2nd fish (4 mins),
take the 1st fish out, put the 2nd fish in, wait (3 mins), take the 2nd fish out.
题意
有
n
n
n条鱼等着你去钓,你可以自定义钓每条鱼的顺序,但是钓每条与都需要花费
k
k
k分钟。钓到鱼之后你还需要把?煮熟,煮熟每条鱼花费的时间是
t
i
t_i
ti可是你只有一口锅,而且锅很小,每次只能装下一条鱼。你在钓鱼时是什么都不能做的,也即是说一旦开始钓鱼就必须在k分钟后才能停下来。当你停下来时如果锅里的鱼还没熟,你不能把鱼拿出来,你可以选择等一会,等到鱼熟了再把新的鱼放进去煮,也可以选择把生鱼存起来直接去钓下一条鱼。问你从开始钓鱼到把所有的鱼煮熟需要花费多少时间。
刚开始的思路是模拟,可是试了几个样例之后发现找不到一个完美的方案来寻找最优解。
正当我准备放弃时,一个想法冒了出来,如果煮鱼的时间大于二倍的钓鱼的时间,我完全可以在一条鱼还没煮熟的时候钓到两条鱼,这样钓完所有的鱼之后你就可以看着锅,把煮鱼时间小于钓鱼时间的鱼放到最后煮,煮熟了之后立刻换下一条鱼。
而中间只需计算出煮鱼的时候能钓几条鱼就行了。同时把没处理的,不够凑成k的零碎时间加入优先队列,如果煮鱼的时候没有钓到足够的鱼,那么就不断从优先队列里取顶元素来占用一个k的时间,最后把剩下的可以一直看着煮的时间加起来就是答案啦~
2019/8/25更新
感觉解释的不够详细。大概思路是这样
先把所有炖鱼时间大于钓鱼时间的鱼找出来,因为它们能一边炖一边钓其他的鱼而不会浪费时间,你就是一边炖一边看着什么也不敢也不会节省时间。
但是那些炖鱼时间小于钓鱼时间的鱼是可以节省时间的——如果你一边炖一遍钓鱼那么鱼炖好之后和钓完鱼之前的时间是被浪费掉了的;
而如果你提前钓完了所有的鱼,你就可以专门看着锅什么也不干,鱼一炖好马上换鱼就而可以充分利用时间。
而我们要做的就是先统计出炖鱼的时间能不浪费时间地钓出多少鱼——即一边炖一边钓,
然后把这些鱼的剩下的炖鱼时间
(
t
i
%
k
)
(t_i \% k)
(ti%k)当作一条条小鱼,放在后面“集中照顾”。
从小到大排序后如果还是钓不完所有的鱼(如上图),那就从优先队列里不断取顶元素(最大的)用来填充钓鱼时间,尽量把最小的炖鱼时间集中在一起。
代码
#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0; i < (a); i++)
#define _rep(i, a, b) for(int i = (a); i<= (b); i++)
#define maxn 10035
#define inf 0x3f3f3f3f
typedef unsigned long long ll;
using namespace std;
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int T;
scanf("%d", &T);
while (T--) {
int n, k;
scanf("%d%d", &n, &k);
ll tim = k;
int num = 0;
priority_queue<int> Q;
_for(i, n) {
int x;
scanf("%d", &x);
tim += (x / k * k);
Q.push(x % k);
}
int tem = tim / k;
while (tem < Q.size()) {
tim += k;
Q.pop();
}
while (Q.size()) {
tim += Q.top();
Q.pop();
}
printf("%I64d\n", tim);
}
return 0;
}
/*
测试:
8
3 4
9 2 2
3 4
5 5 5
4 4
5 6 7 2
3 4
7 6 2
3 4
7 2 2
5 4
12 1 1 1 1
5 4
15 2 2 2 2
4 4
3 1 1 1
答案:
17
19
24
19
16
23
28
17
*/