假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。
本题要求输出前来等待服务的N位顾客的平均等待时间、最长等待时间、最后完成时间,并且统计每个窗口服务了多少名顾客。
输入格式:
输入第1行给出正整数N(≤1000),为顾客总人数;随后N行,每行给出一位顾客的到达时间T
和事务处理时间P
,并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数K(≤10),为开设的营业窗口数。这里假设每位顾客事务被处理的最长时间为60分钟。
输出格式:
在第一行中输出平均等待时间(输出到小数点后1位)、最长等待时间、最后完成时间,之间用1个空格分隔,行末不能有多余空格。
在第二行中按编号递增顺序输出每个窗口服务了多少名顾客,数字之间用1个空格分隔,行末不能有多余空格。
输入样例:
9
0 20
1 15
1 61
2 10
10 5
10 3
30 18
31 25
31 2
3
输出样例:
6.2 17 61
5 3 1
思路及其分析:
思路:
用队列装客户的数据,我们将通过程序输入的窗口个数,将其表达出来
由于需要等待时间和最后的结束时间,可以定义一个当前时间的变量进行计算
需要在窗口寻找事务处理时间最小的用户,对其进行处理,顺便通过当前时间和出队客户的到达时间计算等待时间
在处理过程中会出现 客户到达时间会大于 当前时间的情况 而此时窗口会存在多个的空闲状态需要对此讨论选出最小序号的窗口
在最后队列为空需要在窗口找到处理时间最大的来计算结束时间
分析:
以输出样例为例
我们直接取出队列三个用3个窗口对他们进行服务
在此时发生了两个变化
1.newTime(单词错了别介意 捂脸) = 1, 此时窗口0的事务处理时间发生变化 20 -> 19 平衡了时间在代码中他可以对等待时间可以做出一个很好的计算
newTime += window[min].getProcessingTime();//更新当前时间,加上窗口中事务处理最小时间
//对其他窗口进行平衡时间,为了让窗口的事务处理时间减少从而更好的更新当前的时间和对空档期的判断
EquilibrateTime(window, min);
private static void EquilibrateTime(customer[] window, int min) {//平衡窗口的事务处理时间
customer customer = window[min];
for (int i = 0; i < window.length; i++) {
if (window[i] != customer) {
window[i].setProcessingTime(window[i].getProcessingTime() - customer.getProcessingTime());//直接减因为此时是最小的事务处理时间了不可能会小于零
}
}
}
2.窗口2进入的客户事务处理时间由 61->60 原因为超过了最大时间60分钟
ProcessingTime = Math.min(ProcessingTime, 60);
在此时就应该寻找窗口事务处理最小值,即窗口1的事务结束,再使队列中的一个客户出队
即
int min = findMin(window);//寻找窗口现在事务处理时间最小的客户
customer newCustomer = list.get(0);//出队
list.remove(0);
private static int findMin(customer[] window) {//寻找到达时间最小的客户
int min = 0;
for (int i = 0; i < window.length; i++) {//遍历寻找客户最小到达时间
customer customer = window[i];
if (window[min].getProcessingTime() > customer.getProcessingTime()) {
min = i;
}
}
return min;//最后返回
}
nowTime = 16
出队客户进入窗口1, 以此循环
此时出现了一个问题
当前时间小于即将出队客户的到达时间的此时就出现了一个窗口空档期
因此就需要将 当前时间 变为 即将出队客户的到达时间,同时对窗口内的事务处理时间平衡
即
int IntervalTime = newCustomer.getArrivalTime() - newTime;//空挡期时间多少
for (int i = 0; i < window.length; i++) {
//for循环对窗口的事务处理时间减少,时间都是大家的
int ProcessingTime = window[i].getProcessingTime() - IntervalTime;
//小于0的时间为零免得使下面等待时间算错
window[i].setProcessingTime(Math.max(ProcessingTime, 0));
}
newTime = newCustomer.getArrivalTime();//更新当前时间,为当前要处理客户的到达时间
min = findMin(window);//此时需要再次寻找窗口现在事务处理时间最小的客户,目的是选出最小序号的空闲窗口
在窗口0的是 由于要进入序号最小窗口 所以不在窗口1
之后就正常结束
此时队列就为空了,最大等待时间和等待的总时间都可以算出来了,现在就结束时间没得出
此时就需要在窗口中寻找最大的事务处理时间,从而算出结束时间
//此时退出循环则说明队列已经没有客户了,我们直接找到窗口事务处理时间最大的客户更新当前时间 为最后的结束时间
int max = findMax(window);
newTime += window[max].getProcessingTime();
代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
ArrayList<customer> list = new ArrayList<>();//创建一个客户类的列表实际是个队列
for (int i = 0; i < N; i++) {//获取输入
int ArrivalTime = sc.nextInt();
int ProcessingTime = sc.nextInt();
ProcessingTime = Math.min(ProcessingTime, 60);//判断是否会超过60分钟的最大时间
list.add(new customer(ArrivalTime, ProcessingTime));//将客户添加到列表里
}
int group = sc.nextInt();//输入窗口个数
methode(list, group, N);//开始服务
}
private static void methode(ArrayList<customer> list, int group, int N) {
int[] visited = new int[group];//创建一个对窗口服务数量记录的数组
customer[] window = new customer[group];//创建窗口
//对窗口数据初始化
for (int i = 0; i < window.length; i++) {
window[i] = new customer(0, 0);
}
Arrays.fill(visited, 0);
int newTime = 0;//当前时间 通过这个判断是否有窗口服务的空挡期存在
int MaxWaitingTime = 0;//最大等待时间
int waitingSumTime = 0;//等待总时间
while (!list.isEmpty()) {//判断队列中是否顾客还在排队
int min = findMin(window);//寻找窗口现在事务处理时间最小的客户
customer newCustomer = list.get(0);//出队
list.remove(0);
//该判段为中间是否存在窗口服务的空挡期
//最主要的是选择序号最小的窗口
//如果客户的到达时间大于 现在时间 加上 窗口中事务处理最小时间
//大于 则存在空档期 当前时间 需要更新为当前要处理客户的到达时间
//小于 则 当前时间 加上窗口中事务处理最小时间
if (newCustomer.getArrivalTime() > newTime + window[min].getProcessingTime()) {
int IntervalTime = newCustomer.getArrivalTime() - newTime;//空挡期时间多少
for (int i = 0; i < window.length; i++) {
//for循环对窗口的事务处理时间减少,时间都是大家的
int ProcessingTime = window[i].getProcessingTime() - IntervalTime;
//小于0的时间为零免得使下面等待时间算错
window[i].setProcessingTime(Math.max(ProcessingTime, 0));
}
newTime = newCustomer.getArrivalTime();//更新当前时间,为当前要处理客户的到达时间
min = findMin(window);//此时需要再次寻找窗口现在事务处理时间最小的客户,目的是选出最小序号的空闲窗口
} else {
newTime += window[min].getProcessingTime();//更新当前时间,加上窗口中事务处理最小时间
//对其他窗口进行平衡时间,为了让窗口的事务处理时间减少从而更好的更新当前的时间和对空档期的判断
EquilibrateTime(window, min);
}
window[min] = newCustomer;//服务出队的客户
visited[min]++;//当前窗口服务数加1
int waitingTime = newTime - newCustomer.getArrivalTime();//等待时间
waitingSumTime += waitingTime;//更新等待时间的总时间
MaxWaitingTime = Math.max(waitingTime, MaxWaitingTime);//更新最大时间
}
//此时退出循环则说明队列已经没有客户了,我们直接找到窗口事务处理时间最大的客户更新当前时间 为最后的结束时间
int max = findMax(window);
newTime += window[max].getProcessingTime();
//据题意输出
System.out.printf("%.1f %d %d\n", 1.0 * waitingSumTime / N, MaxWaitingTime, newTime);
for (int i = 0; i < visited.length; i++) {
if (i == 0) {
System.out.print(visited[i]);
} else {
System.out.print(" " + visited[i]);
}
}
}
private static int findMin(customer[] window) {//寻找到达时间最小的客户
int min = 0;
for (int i = 0; i < window.length; i++) {//遍历寻找客户最小到达时间
customer customer = window[i];
if (window[min].getProcessingTime() > customer.getProcessingTime()) {
min = i;
}
}
return min;//最后返回
}
private static int findMax(customer[] window) {//找到窗口事务处理时间最大的客户
int Max = 0;
for (int i = 0; i < window.length; i++) {
customer customer = window[i];
if (window[Max].getProcessingTime() < customer.getProcessingTime()) {
Max = i;
}
}
return Max;
}
private static void EquilibrateTime(customer[] window, int min) {//平衡窗口的事务处理时间
customer customer = window[min];
for (int i = 0; i < window.length; i++) {
if (window[i] != customer) {
window[i].setProcessingTime(window[i].getProcessingTime() - customer.getProcessingTime());//直接减因为此时是最小的事务处理时间了不可能会小于零
}
}
}
}
class customer {//客户类
private int ArrivalTime;//到达时间
private int ProcessingTime;//事务处理时间
public customer() {
}
public customer(int ArrivalTime, int ProcessingTime) {
this.ArrivalTime = ArrivalTime;
this.ProcessingTime = ProcessingTime;
}
/**
* 获取
*
* @return ArrivalTime
*/
public int getArrivalTime() {
return ArrivalTime;
}
/**
* 设置
*
* @param ArrivalTime
*/
public void setArrivalTime(int ArrivalTime) {
this.ArrivalTime = ArrivalTime;
}
/**
* 获取
*
* @return ProcessingTime
*/
public int getProcessingTime() {
return ProcessingTime;
}
/**
* 设置
*
* @param ProcessingTime
*/
public void setProcessingTime(int ProcessingTime) {
this.ProcessingTime = ProcessingTime;
}
public String toString() {
return "customer{ArrivalTime = " + ArrivalTime + ", ProcessingTime = " + ProcessingTime + "}";
}
}