数据结构——银行排队问题之单队列多窗口加VIP服务

题目大意:有很多个服务窗口,有一个窗口是VIP窗口,如果这个窗口有空闲,并且排队等待的人里有VIP,那么VIP先去这个窗口办理业务(SORRY,充钱就是了不起),如果果有多个窗口可以选择,顾客会优先选择窗口编号小的。

题目链接:https://pintia.cn/problem-sets/1042354461223579648/problems/1042355242509160453#p-6

在网上看到很多博客评论这道题很恶心,在这里也跟风一下,这道题确实很恶心。 改了很多次思路,最终AC。

 

这道题,推荐自己疯狂尝试后,在看看题解是如何给出的,这道题会帮助你踩出很多的思维漏洞,额。。。说到思维漏洞,还有题意,注意题目中所给的窗口的编号是从0开始的(心粗的一批)。

 

现在这里说一下错思路的最终版本:

在这道题之前有一个单队列多窗口问题如果不知道,点这里,保持那个问题的整体框架不变,对VIP窗口进行特别的判断,当一个用户的选择是VIP窗口时,这时候就需要判断,队列中是否含有VIP会员,如果有的话,去找次的最快窗口,然后输出。

代码实现:

#include <bits/stdc++.h>

using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1000+10;

struct node
{
    int st;
    int work;
    int vip;
    bool operator < (const node&s ) const
    {
        return st<s.st||(st==s.st&&vip>s.vip);
    }
};

struct nodes
{
    int num;
    int endtime;
    nodes() {num=0; endtime=0;}
};

node que[maxn];
nodes w[maxn];
bool used[maxn];

int main()
{
    int n; scanf("%d",&n);

    for(int i=0;i<n;i++)
    {
        scanf("%d %d %d",&que[i].st,&que[i].work,&que[i].vip);
        que[i].work = que[i].work>=60 ? 60: que[i].work;
    }
    sort(que,que+n);

//    for(int i=0;i<n;i++)
//    {
//        cout<<"输出中间值   "<<que[i].st<<"     "<<que[i].work<<"     "<<que[i].vip<<endl;
//    }
    int k,vipw; scanf("%d %d",&k,&vipw);
    vipw++;


    int max_w=0,max_f=0,sumw=0;

    //cout<<"检测"<<endl;
    for(int i=0; i<n; i++)
    {
        if(used[i]) continue;

        bool flag=false;

        int fw=INF,fn=INF;

        for(int j=1; j<=k; j++)
        {
            if(w[j].endtime<=que[i].st)
            {
                used[i]=true;

                w[j].num++;

                w[j].endtime=que[i].st+que[i].work;

                flag=true;

                //cout<<"输出最后窗口    "<<fn<<"   "<<i<<endl;
                break;
            }

            if(fw>(w[j].endtime-que[i].st))
            {
                fw=w[j].endtime-que[i].st;
                fn=j;
            }

        }

       // cout<<"输出过程值           "<<i<<"   "<<fn<<"    "<<flag<<endl;

        if(flag) continue;

        if(fn==1&&que[i].vip==0)
        {

            bool judge=false;
            int pos=0;

            for(int j=i+1;j<n;j++)
            {
                if(i+1<n&&que[j].vip==1&&que[j].st<=w[fn].endtime)
                {
                    judge=true;
                    pos=j;
                    //cout<<"输出搜索位置的值    "<<j<<endl;
                    break;
                }
            }

            if(judge)
            {

                int fw1=INF,fn1=INF;
                for(int j=1; j<=k; j++)
                {
                    if(j==fn) continue;
                    if(fw1>w[j].endtime-que[i].st)
                    {
                        fw1=w[j].endtime-que[i].st;
                        fn1=j;
                    }

                }

                used[pos]=true;
                used[i]=true;

                w[fn].num++;
                w[fn1].num++;

                max_w=max(max_w,w[fn].endtime-que[pos].st);
                max_w=max(max_w,w[fn1].endtime-que[i].st);

                sumw+=(w[fn].endtime-que[pos].st+w[fn1].endtime-que[i].st);

                w[fn].endtime=w[fn].endtime+que[pos].work;
                w[fn1].endtime=w[fn1].endtime+que[i].work;

                //cout<<"gg2      "<<max_w<<endl;

                 //cout<<"输出最后窗口    "<<fn<<"   "<<i<<endl;

            }
            else
            {
                used[i]=true;

                w[fn].num++;

                max_w=max(max_w,w[fn].endtime-que[i].st);

                sumw+=(w[fn].endtime-que[i].st);

                w[fn].endtime=w[fn].endtime+que[i].work;

                //cout<<"gg2      "<<max_w<<endl;
                //cout<<"输出最后窗口    "<<fn<<"   "<<i<<endl;


            }
        }
        else
        {
            used[i]=true;

            max_w=max(max_w,w[fn].endtime-que[i].st);

            w[fn].num++;

            sumw+=(w[fn].endtime-que[i].st);

            w[fn].endtime=w[fn].endtime+que[i].work;

            //cout<<"gg2      "<<max_w<<endl;
            //cout<<"输出最后窗口    "<<fn<<"   "<<i<<endl;


        }


    }

    for(int i=1;i<=k;i++)
        max_f=max(max_f,w[i].endtime);


    //cout<<"输出总的等待时间   "<<sumw<<endl;

    printf("%.1f %d %d\n",sumw*1.0/n,max_w,max_f);


    int isfirst=1;
    for(int i=1;i<k;i++)
    {
        if(!isfirst) printf(" ");
        if(i==vipw)
        {
             printf("%d %d",w[1].num,w[i+1].num);
        }
        else printf("%d",w[i+1].num);

        isfirst=false;
    }


    return 0;
}

但是提交玩成后只能获得25分,然后就开始寻找自己的错误,最后花了两个小时,才滤出了自己的漏洞,这应该也是这道题最恶心的地方。

当一个普通用户选择VIP窗口时,我们执行的操作是让她去寻找另一个次快的窗口,但是这里有个问题,就是那个插队的VIP办理业务非常快,你仍然要在那个VIP窗口排队,然后需要再一次的判断队列中是否有VIP,如果下一个VIP办理业务依旧很快,你仍然在VIP窗口排队,你需要再次考虑。。。。。。如此的循环下去,(PS:这个是一个简单的For循环能够解决,可以自己动手试一试)

 

然后就是正确的思路,暴力枚举时间流逝一i秒一秒的判断,因为最多有1000个人,而且没人办理业务的时间最多是60S,所以总时间最多就是60000,时间复杂度很小。

 

下面给出AC代码:

#include <bits/stdc++.h>

using namespace std;
const int maxn=2000;
const int INF=0x3f3f3f3f;



struct node
{
    int st,et,vip;
};



node que[maxn];
int wd[maxn];
bool used[maxn],te[11][maxn*60];

int main()
{
    int n;  scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d %d %d",&que[i].st,&que[i].et,&que[i].vip);
        que[i].et= que[i].et>=60 ? 60 : que[i].et;
    }

    int k,vipw; scanf("%d %d",&k,&vipw);

    int cnt=n,sumw=0,max_w=0,max_f=0;
    for(int t=0;cnt;t++)
    {

        if(te[vipw][t]==false)
        {

            for(int i=0;i<n;i++)
            {
                if(used[i]||!que[i].vip) continue;
                if(que[i].st>t) break;

                wd[vipw]++;

                max_w=max(max_w,t-que[i].st);

                max_f=max(max_f,t+que[i].et);

                sumw+=(t-que[i].st);

                cnt--;

                used[i]=true;


                for(int j=0;j<que[i].et;j++) te[vipw][t+j]=true;


                break;
            }
        }

        for(int i=0;i<k;i++)
        {
            if(te[i][t]==false)
            {
                for(int j=0;j<n;j++)
                {
                    if(used[j]) continue;
                    if(que[j].st>t) break;

                    wd[i]++;

                    max_w=max(max_w,t-que[j].st);

                    sumw+=(t-que[j].st);

                    cnt--;

                    used[j]=true;

                     max_f=max(max_f,t+que[j].et);

                    for(int h=0;h<que[j].et;h++) te[i][t+h]=true;

                    break;
                }
            }
        }
    }

    printf("%.1f %d %d\n",sumw*1.0/n,max_w,max_f);

    for(int i=0;i<k;i++)
    {
        if(i) printf(" ");
        printf("%d",wd[i]);
    }
    return 0;
}

 

### 银行单队列多窗口服务系统的模拟实现 银行单队列多窗口服务系统的核心在于如何管理顾客的排队逻辑以及分配到各个窗口的服务流程。以下是基于引用中的描述和专业知识,给出的一种可能的设计方案。 #### 1. 数据结构的选择 为了高效地管理和调度顾客,可以采用以下数据结构- **队列**:用于存储等待服务的所有顾客。 - **数/列表**:表示每个窗口的状态及其当前正在处理的任务结束时间。 具体来说,可以通过一个全局队列来维护所有待处理的顾客,并通过记录每个窗口的结束时间来决定何时将下一个顾客分配给某个空闲窗口。 #### 2. 算法设计思路 整个过程分为以下几个部分: ##### (1)初始化阶段 读取输入并解析顾客信息,同时设置初始状态下的各窗口为空闲。 对于每一个窗口 \(i\) 的结束时间为 \(-\infty\) 或者其他负无穷大值,表明其尚未开始任何工作。 ##### (2)核心循环逻辑 不断从队首取出一名顾客尝试安排进入任意可用的一个最早能够接受新客人的位置直到全部人员都被妥善安置为止;如果存在多个候选地点,则按照题目要求选取编号较小的那个作为目标对象执行操作即可[^2]。 ```python from collections import deque, namedtuple Customer = namedtuple('Customer', ['id']) def bank_service_simulation(customers, k): queue = deque([Customer(id=i) for i in customers]) window_end_times = [-float('inf')] * k # 记录每个窗口的结束时间 result_order = [] while queue: current_customer = queue.popleft() earliest_window_index = min(range(k), key=lambda idx: (window_end_times[idx], idx)) service_start_time = max(window_end_times[earliest_window_index], 0) if current_customer.id % 2 != 0: # 奇数号顾客去A类窗口(假设前k//2个) processing_speed_factor = 1 / 2 else: # 偶数号顾客去B类窗口 processing_speed_factor = 1 service_duration = 1 * processing_speed_factor finish_time = service_start_time + service_duration window_end_times[earliest_window_index] = finish_time result_order.append(current_customer.id) return result_order ``` 上述代码实现了基本的功能需求,其中 `bank_service_simulation` 函数接收两个参数——顾客ID列表与窗口数量\(k\) ,返回最终完成业务顺序的结果向量。 #### 3. 输出结果 根据前面定义好的规则依次打印出每位成功办结手续后的个人身份标识符形成字符串形式输出即满足题意所需答案[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值