hdu6709 Fishing Master

可能对于这类题,有画图说明一下理解起来会比较方便吧,但是鉴于毛毛雨太菜……
咳咳,不如手动画啦!

题目大意

一个人要钓k条鱼,钓每条鱼的时间为k,钓完后要把鱼炖好,炖第i条鱼的时间为ti。要求每次只能炖一条鱼,并且钓鱼期间不能做其他事情,且中途不能停止。问炖好所有鱼最短时间。题目传送门

思路

由于钓鱼期间不能做其他事情,所以钓鱼过程中可能会有时间浪费掉(即人在钓鱼,但是锅里的鱼已经炖好了),我们求最短时间的话,要尽可能让每段炖鱼时间挨在一起,如果不能的话,也要让炖鱼的总间隔时间最短,每次炖鱼的时间内可以钓上来的鱼为k/ti,那么不花费额外时间能钓上来的总鱼数cnt即为k/ti的和,由于一定要先钓上来一条鱼才可以开始炖,所以实际上需要考虑的鱼只有n-1条,用ans记录总时间。

  • 先来考虑最好的情况,不需要花费额外时间,即cnt>=n-1。这种情况意味着每次炖鱼时,都会有源源不断的鱼来补充,不需要再额外花时间去钓鱼, 此时ans为炖鱼总时间。
  • 如果有钓鱼时间长于炖鱼时间,则炖上一条鱼时不一定恰好可以钓上来鱼,则需要花费额外时间来钓鱼,此时即为炖鱼的间隔时间,需要花费额外时间补的鱼的数量为n-1-cnt。
  • 用a数组记录多补一次鱼时需要浪费的时间,a[i]=k-k%ti。为了使浪费时间最短,将a数组从小到大排序,取前n-1-cnt个即可。
  • 总时间即为炖鱼的时间加上浪费掉的时间。

快乐代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100005];
int main()
{
    ll t;scanf("%lld",&t);
    while(t--){
        ll n,k;
        scanf("%lld%lld",&n,&k);
        ll ans=k,cnt=0;
        for(ll i=0;i<n;i++){
            ll tim;scanf("%lld",&tim);
            ans+=tim;
            cnt+=tim/k;
            a[i]=k-tim%k;
        }
        sort(a,a+n);
        if(cnt>=n-1) cout<<ans<<endl;
        else{
            ll len=n-cnt-1;
            for(ll i=0;i<len;i++){
                ans+=a[i];
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

一个奇怪的问题

钓鱼钓了一下午,是因为比赛时候觉得,如果刚刚好钓n条鱼上来的话,会出现钓到某时间鱼不够,而到另一时间鱼突然多了的情况,吃完饭回来问了一下大佬,发现并不会存在这种问题。实际上,在预先知道在炖哪条鱼时要额外花时间钓鱼的话,我们就可以通过调整钓鱼的顺序来保证鱼一直是充足的。甚至可以把所有要多钓的鱼放在最前面钓好。