题目的主要信息:

  • 一个环形的食堂,个入口,入口处排队的人记录在数组a中
  • 刚开始在第一个入口,如果有人在排队,花费1分钟走到下一个入口,如果下一个入口无人可直接进入,否则再花1分钟走到下一个入口,如此循环。(环形食堂,相当于数组a首尾相接)
  • 每过1分钟每个入口排队的人会少1,问最终将从哪个入口进入

方法一:暴力模拟

具体做法:
我们可以设置一个变量i记录下标,设置一个变量time记录过去的时间,从第一个入口即下标i为0且time为0开始,我们检查是否大于0,如果大于0,说明这个时间这个入口还有人在排队,否则这个口可以直接进入;如果不能直接进入,我们时间加1,下标加1(如果是到了数组a的末尾则置回0),如此循环直到找到第一个小于等于0的入口。最后入口的序号是下标
值得注意的是,我们先检查的情况只能从入口1进入,的情况,只能从人数较少的入口进入。

class Solution {
public:
    int nowcoderQueue(int n, vector<int>& a) {
        if(n == 1) //只有1个入口,必定从1号进入
            return 1;
        if(n == 2){ //两个入口就从人少的那个进入
            if(a[0] < a[1])
                return 1;
            else
                return 0;
        }
        int i = 0; //记录入口的下标
        int time = 0; //记录等待的时间
        while(true){
            if(a[i] - time <= 0) //检查这个时间这个入口是否有人
                break;
            else{
                time++; //时间增加
                if(i != n - 1) //下标增加
                    i++;
                else
                    i = 0; //下标循环
            }
        }
        return i + 1; //返回的入口是最后的下标+1
    }
};

复杂度分析:

  • 时间复杂度:,最坏情况下出现一个无人排队的情况需要循环次,在此基础上最坏再循环次找到这个入口,所以复杂度不超过这个
  • 空间复杂度:,常数级变量使用

方法二:按循环计算

具体做法:
因为这个是循环计算的,我们不必每次循环都去算,只需要计算每个数减少到0我们再次经过它需要循环的次数,取次数最少的就可以了,非第一个入口计算时我们还要减掉它的下标,因为它前面的在减少时他也在同步减少,要去掉这个偏差。我们公式计算时,先加再计算除法,保证向上取整。

图片说明

class Solution {
public:
    int nowcoderQueue(int n, vector<int>& a) {
        int res = 0;
        int m = (a[0] + n - 1) / n; //第一个数需要多少次循环到0
        for(int i = 1; i < n; i++){
            if(m > (a[i] - i + n - 1) / n){ //每个数减少到0的时间如果更小,因为前序关系必须严格大于符号
                m = (a[i] - i + n - 1) / n; //更迭更小的循环
                res = i;
            }
        }
        return res + 1;
    }
};

复杂度分析:

  • 时间复杂度:,一次遍历
  • 空间复杂度:,常数级空间