小韦老师@NOIP 普及组-2010-接水问题
题目:
描述
学校里有一个水房,水房里一共装有 m 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为 1。 现在有 n 名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从1 到 n 编号,i 号同学的接水量为 wi。接水开始时,1 到 m 号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学 j 完成其接水量要求 wj 后,下一名排队等候接水的同学 k 马上接替 j 同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即 j 同学第 x 秒结束时完成接水,则 k 同学第 x+1 秒立刻开始接水。若当前接水人数 n’不足 m, 则只有 n’个龙头供水,其它 m−n’个龙头关闭。 现在给出 n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。
输入
第 1 行 2 个整数 n 和 m,用一个空格隔开,分别表示接水人数和龙头个数。 第 2 行 n 个整数 w1、w2、……、wn,每两个整数之间用一个空格隔开,wi 表示 i 号同学的接水量。
输出
输出只有一行,1 个整数,表示接水所需的总时间。
输入样例1
5 3
4 4 1 2 1
输出样例1
4
输入样例2
8 4
23 71 87 32 70 93 80 76
输出样例2
163
提示
1 ≤ n ≤ 10000,1 ≤ m ≤ 100 且 m ≤ n;
1 ≤ wi ≤ 100。
题解:
破题:
对于样例1,可以这样去理解:
第 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 号同学接完水,即所有人完成接水。
思路:
整体思路:
根据上面的破题,不难想到:
我们可以只关注每个水龙头的需求量,例如某个水龙头当前的人接完之后,这时需求量为 0,将下一个接替的同学的接水量作为这个水龙头的新的需求量。当所有人都接完水(也可以理解成所有水龙头的需求量都变成 0,并且没有人可以接替),所花的时间即为所求。
我们可以模拟每一秒的水龙头的需求量,一开始的时候,1~m 号同学分别在 1~m 号水龙头接水,当过了一秒之后,每个水龙头的需求量减少 1,这时若某个水龙头的需求量变成 0,则应该由 m+1 号同学接替,若还有其他水龙头的需求量变为 0,则依次由后面的同学(m+2 号,m+3 号……n 号) 进行接替,直到所有人都接替完了,并且所有水龙头的需求量都为 0,即为结束的时间。
刚开始时 1~m 个水龙头默认的对应 1~m 编号同学的接水量,一旦有一个同学接完了 那么就让下一个等待接水的同学来这个水龙头接水。
用代码实现的话就是让下个同学对应的接水值变成当前这水龙头的需求量。
具体步骤:
1.定义一个数组:
const int N = 1e4 + 110;
int w[N]; // 每位同学的接水量
2.定义三个变量 n, m, Time,并输入 n 和 m:
int n, m, Time; // 人数,水龙头数,当前所花时间
cin >> n >> m;
3.输入 n 位同学的接水量
for (int i = 1; i <= n; i++) {
cin >> w[i];
}
4.定义一个变量 num,用来表示下一个接水的同学
int num = m + 1; // 示下一个接水的同学,初始化为 m + 1
5.num 的初始值为 m + 1 因为一开始编号为 1~m 的同学在接水,下一位同学就是 m + 1,因为 n 个同学都接完水会让 num 最终加 n 个 1,所以最终 num 的值应该为 m + n + 1,也就意味着 num = m + n + 1 时意味着所有同学都接完水了。
故,当 num <= n + m 时,做以下操作:
- 对于 1~m 号水龙头,每一秒都将需求量减 1,若当前的水龙头需求量为 0,则当前的需求量为下
一位等待接水的同学的接水量,并将下一个等待接水的同学的编号加 1。
for (int i = 1; i <= m; i++) {
w[i]--;
if (w[i] == 0) {
w[i] = w[num];
num++;
}
}
- 时间加 1 秒(其实上面的 1~m 号水龙头的接水是在 1 秒内完成的)
Time++;
6.输出时间 Time。
思考:
1°为什么 w 数组的大小是 10000 + 110?
2°num 为什么要初始化为 m + 1?
3°为什么 num = m + n + 1 时意味着所有同学都接完水了?
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 110;
int w[N]; // 每位同学的接水量
int main() {
int n, m, Time; // 人数,水龙头数,当前所花时间
cin >> n >> m;
for (int i = 1; i <= n; i++) { // 输入 n 位同学的接水量
cin >> w[i];
}
int num = m + 1; // 示下一个接水的同学,初始化为 m + 1
while (num <= m + n) { // 当 num <= m + n 时
for (int i = 1; i <= m; i++) { // 枚举每个水龙头
w[i]--; // 当前水龙头的需求量减 1
if (w[i] == 0) { // 若当前的需求量为 0
w[i] = w[num]; // 则将等待接水的下一位同学的接水量赋给当前水龙头的需求量
num++; // 等待接收的同学的编号加 1
}
}
Time++; // 处理完 1~m 个水龙头,时间过去一秒(理论上应该同时处理的)
}
cout << Time; // 输出时间
return 0;
}