1052. 爱生气的书店老板(滑动窗口(有优化))

爱生气的书店老板

今天,书店老板有一家店打算试营业 customers.length 分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。

书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 X 分钟不生气,但却只能使用一次。请你返回这一天营业下来,最多有多少客户能够感到满意的数量。

示例:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3
输出:16
解释:
书店老板在最后 3 分钟保持冷静。
感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.


解题思路

滑动窗口(双指针):left 指针,开始时指向数组的开头(窗口的长度是固定的,大小为 X),right 指针指到 X - 1 的位置。

(1) 首先,将在老板没有使用技能时就已经的满意的顾客的总人数计算出来(因为,当用了技能后,之前不满意的顾客可能会变得满意,而之前已经满意的顾客对书店的态度则一定不会发生改变),然后对 customers 数组中的当前窗口进行遍历,每遍历一个值就判断在这个时刻老板是否生气,若老板生气则直接将人数加到 Nums (用来记录滑动窗口中因老板生气而不满意的顾客的人数)中。

(2) 然后,老板使用技能,选连续的 X 分钟让自己不生气(在这 X 分钟内,不满意的顾客的人数应该是最多的),则利用滑动窗口,找到在数组中长度为 X 并且不满意的顾客的人数最多的子数组。

(3) 最后,将满意的人数和第 (2) 步中得到的不满意的人数相加,返回最多满意的顾客数。

class Solution {

    public int maxSatisfied(int[] customers, int[] grumpy, int X) {

        int n = customers.length;
        int left = 0, right = X - 1;
        int sum = 0, maxNums = 0, Nums = 0;

        for (int i=0;i<n;i++) {

            if (grumpy[i] == 0) {

                sum += customers[i];
            }
        }
        while (right < n) {

            Nums = 0;
            for (int i=left;i<=right;i++) {

                if (grumpy[i] == 1) {

                    Nums += customers[i];
                }
            }
            maxNums = Math.max(maxNums, Nums);
            right++;
            left++;
        }
        return sum + maxNums;
    }
}

时间复杂度:O(n²)
空间复杂度:O(1)

优化1

在开始时对满意的人数进行累加时,将已经加过的老板不生气时满意的人数全都修改为 0。这样当在进行 while 循环找因为老板生气而不满意的顾客人数时,不需要对老板是否生气进行判断,因为老板不生气的时间顾客满意的人数均已修改为0。

class Solution {

    public int maxSatisfied(int[] customers, int[] grumpy, int X) {

        int n = customers.length;
        int left = 0, right = X - 1;
        int sum = 0, maxNums = 0, Nums;

        for (int i=0;i<n;i++) {

            if (grumpy[i] == 0) {

                sum += customers[i];
                customers[i] = 0;
            }
        }
        while (right < n) {

            Nums = 0;
            for (int i=left;i<=right;i++) {

            	Nums += customers[i];
            }
            maxNums = Math.max(maxNums, Nums);
            right++;
            left++;
        }
        return sum + maxNums;
    }
}

时间复杂度:有循环的嵌套,并且最坏的情况就是滑动窗口就是整个 customers 数组,长度为 n,所以时间复杂度为 O(n² + n) = O(n²)
空间复杂度:没有额外的开辟新的空间,所以复杂度为O(1)

优化2

去掉了两个循环的嵌套,使程序的执行时间大大减少

判断 right 指针指向的位置老板是否生气:若生气,则记录下此刻进店的人数 Nums;若不生气,则判断当前窗口的大小和老板使用技能的时间 X:若窗口大小 <= X,则此时直接进行最大值的判断,并且 right 指针向右移动一位;若窗口大小 > X,则此时要缩小窗口,判断当前 left 指针指向的位置老板是否生气:若不生气,则直接将 left 指针向右移动一位即可;若生气,则要从 Nums 中减去当前 left 指针所指向的人数,再将 left 指针向右移动一位,然后进行最大值的判断,并且 right 指针向右移动一位,继续进行下面的判断。

class Solution {

    public int maxSatisfied(int[] customers, int[] grumpy, int X) {

        int n = customers.length;
        int left = 0, right = 0;
        int sum = 0, maxNums = 0, Nums = 0;

        for (int i=0;i<n;i++) {

            if (grumpy[i] == 0) {

                sum += customers[i];
            }
        }
        while (right < n) {

            if (grumpy[right] == 1) {

                //若此时老板生气,则直接记录此刻的进店人数
                Nums += customers[right];
            }
            //当前窗口的大小比老板使用技能时间长,则要减小窗口大小
            if (right - left + 1 > X) {

                if (grumpy[left] == 1) {

                    Nums -= customers[left];
                }
                left++;
            }
            maxNums = Math.max(maxNums, Nums);
            right++;
        }
        return sum + maxNums;
    }
}

时间复杂度:只是遍历了两次 customers 数组且长度为 n,所以时间复杂度为 O(n + n) = O(2n) = O(n)。(常数不考虑在内)
空间复杂度:没有额外的开辟新的空间,所以复杂度为O(1)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值