codevs 3377 [Mz]接水问题2

3377 [Mz]接水问题2
时间限制: 1 s
空间限制: 64000 KB
题目等级 : 钻石 Diamond
题解
查看运行结果
题目描述 Description
学校里有一个水房,水房里一共装有m个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为1。

现在有n名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从 1到n编号,i号同学的接水量为wi。接水开始时,1到m号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学j完成其接水量要求wj后,下一名排队等候接水的同学k马上接替j同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即j同学第x秒结束时完成接水,则k同学第x+1秒立刻开始接水。若当前接水人数n’不足m,则只有n’个龙头供水,其它m-n’个龙头关闭。

现在给出n名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。

特别地,同学们在打水前排好了队,接水所用时间更长的先接。

(ps:出题人本来想设计一个贪心的题目,然后发现用贪心做有反例,只能强改题目,在此声明道歉。不要在意样例解释。)

输入描述 Input Description
第 1 行2 个整数 n 和 m,用一个空格隔开,分别表示接水人数和龙头个数。

第 2 行 n 个整数 w1、w2、„„、wn,每两个整数之间用一个空格隔开,wi表示 i 号同学的接水量。

输出描述 Output Description
输出只有一行,1 个整数,表示接水所需的总时间。

样例输入 Sample Input
5 3

4 4 1 2 1

样例输出 Sample Output
4

思路:
模拟性质的堆
先排序,从大到小
然后把前m个丢进堆里
每次取出最小的,加上正在排队的最大的,丢回去
随手维护max
输出max
小技巧:
随手维护max,可以省去最后一步把堆里的数都丢出来取最大;
stl的优先队列是 priority_queue< xxx > (可以用搜狗输入法打哦)
重定义小根堆 priority_queue<类型,vector<类型>,greater<类型> > (注意最后一个空格)

//优先队列版
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int n,m,num[1000050],tot,maxx;
priority_queue<int,vector<int>,greater<int> > Q;
bool cmp(int a,int b){
    return a>b;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&num[i]);
    sort(num+1,num+n+1,cmp);
    maxx=num[1];
    for(tot=0;tot<m;++tot){
        Q.push(num[tot+1]);
    }
    while(tot<=n){
        int v=Q.top();
        Q.pop();
        v+=num[++tot];
        if(v>maxx) maxx=v;
        Q.push(v);
    }
    printf("%d",maxx);
    return 0;
}
//手打堆版
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000050ll;
int n,m,num[maxn],d[maxn],maxx;
void inswap(int a,int b){
    int tmp=d[a];
    d[a]=d[b];
    d[b]=tmp;
    return ;
}
void inpop(){
    inswap(1,m);
    d[m]=0;
    m--;
    bool bo=1;
    int now=1;
    int minn;
    while(bo&&d[now*2]!=0){

        if(d[now]>d[now*2])
        minn=now*2;
        else minn=now;

        if(d[minn]>d[now*2+1]&&d[now*2+1])
        minn=now*2+1;
        if(minn==now) {
            break;
            bo=0;
        }
        else {
            inswap(now,minn);
            now=minn;
        }
    }

    return ;
}
void inpush(int x){
    m++;
    d[m]=x;
    bool bo=1;
    int now=m;
    while(bo){
        if(d[now]<d[now/2]){
            inswap(now,now/2);
            now=(now/2);
        }
        else{
            break;
            bo=0;
        }
    }
    return ;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&num[i]);
    sort(num+1,num+n+1);
    int tot=n-m;
    for(int i=1;i<=m;i++)
    d[i]=num[tot+i];
    while(tot){
        int a=d[1];
        inpop();

        a=a+num[tot];tot--;
        inpush(a);
    }
    for(int i=1;i<=m;i++)
    maxx=max(maxx,d[i]);
    printf("%d",maxx);
    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值