题目:
银行有m个服务窗口,假设当前有n个人等待办理业务,那么这n个人会被顺序分配一个从1到n的号码。
第i号办理业务的时间都为,一个窗口一次只能办理一个人的业务,上一个人的业务办理完才能办理下一个人的业务,有多少对(i,j),满足,且第i号办理业务完成的时间严格大于第j号办理业务完成的时间。
方法一:优先级队列+暴力
每次先办理完业务的窗口先空出来,因此采用优先级队列存储每个人办理业务的总时间,首先让开始时间为0进队,然后,对于遍历n个人,每次将第i个人的业务开始时间出队(空闲窗口),记录第i个人的业务办理总时间在ans数组中,并将总时间入队,直到所有人的业务办理时间都记录在ans数组中,暴力枚举数组中每个元素为i,累计ans[i]>ans[j]的个数

import java.util.*;


public class Solution {
    /**
     * 求解合法的(i,j)对的数量
     * @param n int整型 n个人
     * @param m int整型 m个窗口
     * @param a int整型一维数组 长度为n的vector,顺序表示1-n号客人的办理业务所需时间
     * @return long长整型
     */
    public long getNumValidPairs (int n, int m, int[] a) {
        // write code here
        PriorityQueue<Long>q=new PriorityQueue<>();
        long[]ans=new long[n];
        for(int i=0;i<m;i++)q.add((long)0);//当前窗口的开始时间入队
        for(int i=0;i<n;i++){
            long time=q.peek();
            q.poll();//窗口空闲出队
            ans[i]=time+a[i];//记录总的时间=开始时间+持续时间
            q.add(ans[i]);//总的时间入队
        }
        long res=0;
        for(int i=0;i<n-1;i++){
            for(int j=i+1;j<n;j++){
                if(ans[i]>ans[j])res++;
            }
        }
        return res;
    }
}

复杂度:
时间复杂度:图片说明 ,记录数组外层循环n次,优先级队列每次出队的时间复杂度为图片说明 ,再加上寻找逆序对个数双层循环的时间复杂度为图片说明 ,暴力解***超时
空间复杂度:,优先级队列的大小不超过m,ans数组大小不超过n
方法二:归并排序+优先级队列
由于直接暴力求解逆序对的时间复杂度过高会导致超时,因此我们可以采用归并排序来计算逆序对的个数
具体做法为:

  • 左指针定位在数组的最左边,右指针定位到数组的最右边,不断找到数组的中点,从中点处分割,直到左右指针相遇,递归终止

  • 将分割出来的每一个部分排序,也就是进入merge_sort函数,因为每一轮排序都是基于上一轮左右数组已排序好的情况下再排序,因此排序完毕后记得将已经排序好的temp部分相应拷贝到a数组部分

  • 排序部分是从中点处分割数组为两部分,逐轮比较两部分的元素,将较小值放入辅助数组中,最后如果两部分中有剩余部分元素,则将剩余部分直接拷贝到中接下来的位置

  • 因为每一轮排序都是基于上一轮左右数组已排序好的情况下再排序,因此每一轮排序中只要出现a[i]>a[j]则a[i]到a[mid]部分都大于a[j],这一轮逆序对个数则等于mid-i+1,累加每一轮逆序对个数则得到整个数组的逆序对个数

图片说明

import java.util.*;


public class Solution {
    /**
     * 求解合法的(i,j)对的数量
     * @param n int整型 n个人
     * @param m int整型 m个窗口
     * @param a int整型一维数组 长度为n的vector,顺序表示1-n号客人的办理业务所需时间
     * @return long长整型
     */

    PriorityQueue<Long>q=new PriorityQueue<>();
    public long getNumValidPairs (int n, int m, int[] a) {
        // write code here
        long[]temp=new long[n];
        long[]ans=new long[n];
        for(int i=0;i<m;i++)q.add((long)0);//入队窗口开始时间
        for(int i=0;i<n;i++){
            long time=q.poll();//出队最短开始时间
            ans[i]=time+a[i];//总时间等于开始时间+持续时间
            q.add(ans[i]);
        }
        return merge_sort(0,n-1,ans,temp);
    }
    long merge_sort(int l,int r,long[]a,long[]temp){
        if(l>=r)return 0;//递归结束条件
        int mid=(l+r)>>1;
        long res=merge_sort(l,mid,a,temp)+merge_sort(mid+1,r,a,temp);
        int cnt=0,i=l,j=mid+1;
        while(i<=mid&&j<=r){
            if(a[i]<=a[j])temp[cnt++]=a[i++];
            else{
                temp[cnt++]=a[j++];
                res+=mid-i+1;//a[i]到mid这段在之前已经排序好,则a[i]>a[j]时,a[i]到mid这段都比a[j]大
            }
        }
        while(i<=mid)temp[cnt++]=a[i++];
        while(j<=r)temp[cnt++]=a[j++];
        for(i=l,j=0;i<=r;i++,j++){
            a[i]=temp[j];//把排序好的元素复制回原数组,为下次寻找逆序对做准备
        }
        return res;
    }
}

复杂度:
时间复杂度:图片说明 ,因为记录数组外层循环n次,优先级队列每次出队的时间复杂度为图片说明 ,归并排序时间复杂度为图片说明 ,图片说明
空间复杂度:,优先级队列的大小不超过m,ans和temp数组大小不超过n,