D. DS队列----银行单队列多窗口模拟

题目

假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾客总是选择编号最小的窗口。

本题要求输出前来等待服务的N位顾客的平均等待时间、最长等待时间、最后完成时间

输入

输入第1行给出正整数N(≤1000),为顾客总人数;随后N行,每行给出一位顾客的到达时间T和事务处理时间P,并且假设输入数据已经按到达时间先后排好了顺序;最后一行给出正整数K(≤10),为开设的营业窗口数。

输出

在一行中输出平均等待时间(输出到小数点后1位)、最长等待时间、最后完成时间,之间用1个空格分隔,行末不能有多余空格。

解析

面对这一道题,一开始我的思路是设置一个全局变量time,无限循环time++来模拟当前时间,以time来选择时间最少的窗口,但是这样会让资源占用过多,万一有一个到达时间很大的顾客,就会导致time一直++才能到这个顾客到达的时间。

后面舍弃了time++的方案,我们创建一个customer顾客类/结构体,有成员变量t(到达时间)、q(处理业务时长)、end(处理结束后的时间点)、wait(等待时长),我们注意时间点和时长的区别。

struct customer{
    int t = 0, p = 0, wait = 0, end = 0;
    // t到达时间点 p处理所需时间 wait等待时间 end当前客户完成业务的’时间点‘
}

接收完输入的顾客信息(customer)我们保存在一个队列(queue)q中,并且创建k个处理窗口window,每一个窗口均为一个队列,并且初始化每一个窗口中有一个数值均为0的顾客。

queue<customer> windows[k]; // k个处理窗口
    for(int i=0;i<k;i++){
        customer c;
        windows[i].push(c);
    } // 每个窗口都为一个队列,初始化每个窗口有一位所有数值都为0的顾客

后我们开始循环取出队列第一个顾客,开始插入windows窗口中处理,我们创建min(最短时间)、time(当前队列的时间)、index(最短时间的队列下标)变量,若当前队列的最后一个顾客的end(处理完的时间点)最小,即将当前顾客推进push到当前队列,并计算当前顾客处理完的时间点end与等待时间wait为多少。

 注意到:我们计算当前顾客end时,我们需要结合上一位顾客的end时间点判断,假如我们的到达时间点t若晚于>上一个顾客处理完的时间点end,那我们的end自然时t(到达时间点)+p(处理完所需时长);假如我们到达时间的时候上一个顾客还没处理完(到达时间点t若早于<上一个顾客处理完的时间点end),我们就需要等到他处理完才能开始处理我们的事务,所以当前end等于上一个顾客的end + 我们处理事务的时间 (即 end = 上一个end + p)。

    while (!q.empty()){ // 开始循环顾客队列插入处理窗口
        int min = windows[0].back().end, time = 0, index = 0;
        // 判断哪一个窗口队列最后一个顾客的结束时间点end最小,就记录下当前窗口下标
        for(int i=0;i<k;i++){
            time = windows[i].back().end;
            if(time < min){
                min = time;
                index = i;
            }
        }
        q.front().end = q.front().t >= windows[index].back().end ? q.front().t+q.front().p : windows[index].back().end+q.front().p;
        // 计算当前顾客的结束时间点,要是当前顾客的到达时间晚于上一个顾客的end,就end = 到达时间 + 处理时间
        // 否则当前的end必须要从前一个顾客的end开始才可以处理,所以当前end = 上一个end + 处理时间
        q.front().wait = q.front().end - q.front().p - q.front().t;
        // 当前顾客的等待时间就是 当前结束时间点 减去 到达时间点 和 处理时间
        windows[index].push(q.front());
        q.pop();
    }

代码整体

#include <iostream>
#include <queue>
#include <iomanip>
using namespace std;

struct customer{
    int t = 0, p = 0, wait = 0, end = 0;
    // t到达时间点 p处理所需时间 wait等待时间 end当前客户完成业务的’时间点‘
};

int main() {
    int n, t, p, k; // n客户数量 t和p用来暂时储存每个顾客的 到达时间 和 处理所需时间 k为处理窗口数量
    queue<customer> q; // 顾客队列
    cin >> n;

    for(int i=0;i<n;i++){
        cin >> t >> p;
        struct customer cus = {t, p};
        q.push(cus);
    }
    cin >> k;

    queue<customer> windows[k]; // k个处理窗口
    for(int i=0;i<k;i++){
        customer c;
        windows[i].push(c);
    } // 每个窗口都为一个队列,初始化每个窗口有一位所有数值都为0的顾客

    while (!q.empty()){ // 开始循环顾客队列插入处理窗口
        int min = windows[0].back().end, time = 0, index = 0;
        // 判断哪一个窗口队列最后一个顾客的结束时间点end最小,就记录下当前窗口下标
        for(int i=0;i<k;i++){
            time = windows[i].back().end;
            if(time < min){
                min = time;
                index = i;
            }
        }
        q.front().end = q.front().t >= windows[index].back().end ? q.front().t+q.front().p : windows[index].back().end+q.front().p;
        // 计算当前顾客的结束时间点,要是当前顾客的到达时间晚于上一个顾客的end,就end = 到达时间 + 处理时间
        // 否则当前的end必须要从前一个顾客的end开始才可以处理,所以当前end = 上一个end + 处理时间
        q.front().wait = q.front().end - q.front().p - q.front().t;
        // 当前顾客的等待时间就是 当前结束时间点 减去 到达时间点 和 处理时间
        windows[index].push(q.front());
        q.pop();
    }

    int f = 0, max = 0, end = 0;
    double wait = 0;


    while (!windows[f].empty()){
        customer cus = windows[f].front();
//        cout << "到达时间:" << windows[f].front().t << " 所需时间:" << windows[f].front().p << " 等待时间:" << windows[f].front().wait <<  " 结束时间:" << windows[f].front().end << " || ";
        wait += cus.wait;
        max = max < cus.wait ? cus.wait : max;
        end = end < cus.end ? cus.end : end;
        windows[f].pop();
        if(windows[f].empty()&&f!=k-1){
            f++;
        }
    }

    cout << fixed << setprecision(1) << wait/n << " ";
    cout << max << ' ' << end << endl;

    return 0;
}