1127 接水问题

2010年NOIP全国联赛普及组

题目描述 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 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。

输入描述 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

数据范围及提示 Data Size & Hint

n<=10000, m<=100

第1 秒,3 人接水。第1 秒结束时,1、2、3 号同学每人的已接水量为1,3 号同学接完水,4 号同学接替3 号同学开始接水。
第2 秒,3 人接水。第2 秒结束时,1、2 号同学每人的已接水量为2,4 号同学的已接水量为1。
第3 秒,3 人接水。第3 秒结束时,1、2 号同学每人的已接水量为3,4 号同学的已接水量为2。4 号同学接完水,5 号同学接替4 号同学开始接水。
第4 秒,3 人接水。第4 秒结束时,1、2 号同学每人的已接水量为4,5 号同学的已接水量为1。1、2、5 号同学接完水,即所有人完成接水。
总接水时间为4 秒。

问题分析

直接模拟正常的排队打水的过程:前m个人依次上前各自使用一个水龙头。

从第m+1个人开始,每个人都要考虑应该使用哪一个水龙头,具体决策的依据是:每次都选择目前最早结束工作的那个水龙头。

所以,w[ ]前m个元素不动,直接用来记录m个水龙头目前还需要工作的时长。

 1 #include <stdio.h>
 2 int findMin(int *a,int begin,int end);//返回a[begin]~a[end]之间的最小值的下标 
 3 int findMax(int *a,int begin,int end);//返回a[begin]~a[end]之间的最大值的下标
 4 int main(int argc, char *argv[])
 5 {
 6     int n,m,w[10005]={0},i,t;
 7     scanf("%d%d",&n,&m);
 8     for(i=0;i<n;i++)
 9         scanf("%d",&w[i]);
10     for(i=m;i<n;i++)
11     {
12         t=findMin(w,0,m-1);
13         w[t]=w[t]+w[i];
14     }
15     
16     t=findMax(w,0,m-1);
17     printf("%d
",w[t]);
18     return 0;
19 }
20 int findMin(int *a,int begin,int end)//返回a[begin]~a[end]之间的最小值的下标 
21 {
22     int i,minI=begin;
23     for(i=begin+1;i<=end;i++)
24     {
25         if(a[i]<a[minI]) minI=i;
26     }
27     return minI;
28 }
29 int findMax(int *a,int begin,int end)//返回a[begin]~a[end]之间的最大值的下标
30 {
31     int i,maxI=begin;
32     for(i=begin+1;i<=end;i++)
33     {
34         if(a[i]>a[maxI]) maxI=i;
35     }
36     return maxI;
37 }

  这里没有考虑一种特殊的情况:水龙头的数量比人数多的情况,题目的测试数据也没有这种情况。若是存在这个情况,那么答案就是直接找出w[ ]的最大值即可。

另一种思路 参照https://www.cnblogs.com/xianyue/p/7040537.html

这道题也可以使用优先队列(或者说“最小堆”)去做。优先队列在C++的queue库中有一个priority_queue的实现。

我们首先将m个数放入优先队列,然后每次从其中取出一个最小的数,再将这个数加上接下来要进去的那个人k对应的数(即为k离开的时间)。最后我们将优先队列里面的所有数取出来,其中最大的那个数就是答案。

 关于有线队列可以阅读这里:https://www.cnblogs.com/Deribs4/p/5657746.html

 1 #include <iostream>
 2 #include <queue>
 3 #include <vector>
 4 using namespace std;
 5 priority_queue<int, vector<int>, greater<int> > que;
 6 int n, m, a[10010], res = 0;
 7 int main()
 8 {
 9     cin >> n >> m;
10     for (int i = 0; i < n; i ++)
11         cin >> a[i];
12     for (int i = 0; i < m; i ++)
13         que.push(a[i]);
14     for (int i = m; i < n; i ++)
15     {
16         int t = que.top();
17         que.pop();
18         que.push(a[i] + t);
19     }
20     while (!que.empty())
21     {
22         int t = que.top();
23         que.pop();
24         if (t > res)
25             res = t;
26     }
27     cout << res << endl;
28     return 0;
29 }
原文地址:https://www.cnblogs.com/huashanqingzhu/p/10445619.html