队列模拟+归并求解

题目描述

排队等号时,观察到以下场景。
银行有m个服务窗口,假设当前有n个人等待办理业务,那么这n个人会被顺序分配一个从1到n的号码。

等待办理业务的流程如下:
从第1号到第n号顺序的进行排队。
假设当前第1号到第i-1号都正在办理或已经办理完业务,且某个窗口A没有客人正在办理业务,那么第i号会马上到窗口A办理他的业务。
如果有多个这样的窗口,第i号会随意选择一个窗口。

0时刻,观察到m个窗口都没有客人正在办理业务,而n个人正在等待办理业务。
为了简化问题,我们假设第i号不管在哪个窗口办理业务,办理业务的时间都为a_ia
有多少对(i,j),满足i<j≤n,且第i号办理业务完成的时间严格大于第j号办理业务完成的时间。

示例1
输入
5,2,[1,3,2,5,4]
返回值
1

说明
第1号 开始办理时间 0 办理完成时间 1
第2号 开始办理时间 0 办理完成时间 3
第3号 开始办理时间 1 办理完成时间 3 (在1号办理完同时开始办理)
第4号 开始办理时间 3 办理完成时间 8 (在2和3号办理完同时开始办理)
第5号 开始办理时间 3 办理完成时间 7 (在2和3号办理完同时开始办理)
唯一一组满足题意的(i,j)对为(4,5)

求解思路

常规思路是,采取time时间推进模拟,即remain_time[]数组存放剩余占用时长,每次时间推进,remain_time修改(remain_time==0表示空闲)同时采取finish_time[]记录完成时刻

该做法的优点是:符合思考与实际推进逻辑,缺点是每次迭代的步长为time+=1,若每个任务的时长很大,将造成很大的时间开销

进阶做法

维护一个小根堆,堆中存放每个窗口下一个可用的时刻,这样堆顶即为:最近一个可用窗口的时刻
初始化:小根堆存放m个0 表示m个窗口在0时刻均可用
依次按照任务序列,从堆中安排任务,安排任务后,可将now_time+a[i]作为完成时刻(同时也是一个下一个可用时刻=》入堆)

求得finish_time[]后,剩下的问题是求解逆序对,采取归并排序的思路,在merge时,若前面部分元素i比后半部分元素j更大=》 ans+=j-start_j 即后半部分j左侧的都能和i构成逆序对

代码实现

 static long long merge_sort(int left,int right,vector<long long>& arr) //对arr进行归并排序 并统计逆序对
    {
        if(left>=right)
            return 0;    //递归的返回点
        int mid=(left+right)/2;
        long long ans=0;
        ans+=merge_sort(left,mid,arr);
        ans+=merge_sort(mid+1,right,arr);  //先将原数组划分左右两边完成排序
        
        //接下来进行合并
        vector<long long> temp(right-left+1);//临时数组
        int k=right-left;
        int i=mid;
        int j=right;   //
        while(i>=left && j>=mid+1)
        {
            if(arr[i]>arr[j])  //前半部分更大   逆序对  只发生在前面部分元素  比  后面部分元素大的情况
            {
                temp[k--]=arr[i--];//填充
                ans+=j-mid; //计算
            }
            else
            {
                temp[k--]=arr[j--]; //后半部分更大
            }
            
        }
        //剩余部分完成填充
        while(i>=left)
        {
            temp[k--]=arr[i--];
        }
        while(j>=mid+1)
        {
            temp[k--]=arr[j--];
        }
        for(k=0;k<right-left+1;k++)   //填充到原数组
            arr[left+k]=temp[k];
        return ans;
    }
    long long getNumValidPairs(int n, int m, vector<int>& a) {
        // write code here
 
        vector<long long> finish_time(n);//完成时间
        //小根堆  里面元素 item的含义是 下一次空闲的时刻  初始化 所有元素开始空闲的时刻均为0
        priority_queue<long long,vector<long long>,greater<long long>> p;
        for(int i=0;i<m;i++)
            p.push(0);
        
        for(int i=0;i<n;i++) //依次处理每一个任务
        {
            long long finsh=p.top()+a[i];// 找到下一个第一个空闲的位置 + 所需时间
            finish_time[i]=finsh;
            p.pop();//弹出这个 可用
            p.push(finsh);//压入下一个可用开始时间
        }
        
        //结束以后  所有任务的完成时刻确认了

        //问题转变为数组finsh_time[] 求解逆序对问题 采取归并的思想
       return merge_sort(0,n-1, finish_time);
  
        
        
    }
相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页