爱生气的书店老板

91 阅读2分钟

题目

滑动窗口

public class Main {
    public static void main(String[] args) {

        Main main = new Main();
        int [] nums = new int[] {1,0,1,2,1,1,7,5};
        int [] nums2 = new int[] {0,1,0,1,0,1,0,1};
        main.maxSatisfied(nums, nums2, 3);
    }



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

        int sum = Arrays.stream(customers).sum();
        if (X == customers.length) {
            return sum;
        }

        // dp[i]记录customers i位置之后, 所有元素的和
        int [] dp = new int[customers.length];
        dp[0] = sum;
        for(int i = 1; i < customers.length; i ++) {
            dp[i] = dp[i - 1] - customers[i - 1];
        }

        // dp2[i] 记录了考虑grumpy后每个位置的和
        int [] dp2 = new int[customers.length];
        int sumPart = 0;
        for (int i = 0; i < customers.length; i ++) {
            if (grumpy[i] == 0) {
                sumPart = sumPart + customers[i];
            }
        }
        dp2[0] = sumPart;
        for(int i = 1; i < customers.length; i ++) {
            if (grumpy[i - 1] == 0) {
                dp2[i] = dp2[i - 1] - customers[i - 1];
            } else {
                dp2[i] = dp2[i - 1];
            }

        }

        // 遍历customers, 以X为窗口, 计算窗口笼罩时, 客流量的最大值
        int left = 0;
        int right = left + X;
        int max = Integer.MIN_VALUE;
        while(right <= customers.length) {
            int tempSum = 0;
            if (left > 0) {
                // 窗口左边
                tempSum = tempSum + dp2[0] - dp2[left];
            }

            // 窗口中间
            if (right == customers.length) {
                tempSum = tempSum + dp[left];
            } else {
                tempSum = tempSum + dp[left] - dp[right];
            }

            // 窗口右侧
            if (right < customers.length) {
                tempSum = tempSum + dp2[right];
            }
            max = Math.max(tempSum, max);
            left ++;
            right = left + X;


        }
        return max;

    }

}

基本思路

  1. 利用一个长度为X的窗口, 计算窗口左边的累加和, 计算窗口的累加和, 计算窗口右边的累加和, 三部分的和就是题目的答案, 取最大值

  2. 提前计算好每个位置到最后的和, 每次计算时查询即可, 无需循环

滑动窗口2

public class Main {
    public static void main(String[] args) {

        Main main = new Main();
        int [] nums = new int[] {4, 10, 10};
        int [] nums2 = new int[] {1, 1, 0};
        main.maxSatisfied(nums, nums2, 2);
    }



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

        if (customers.length == X) {
            return Arrays.stream(customers).sum();
        }
        // 先计算出确定满意的顾客量
        // 再利用X作为滑动窗口, 计算出额外增加的顾客量, 记录窗口内额外增加顾客的最大值

        int sum1 = 0;
        for (int i = 0; i < customers.length; i ++) {
            if (grumpy[i] == 0) {
                sum1 = sum1 + customers[i];
            }
        }

        int left = 0;
        int right = left + X - 1;
        int sum2 = 0;
        int max = 0;
        while (right < customers.length) {
            if (left == 0) {
                // 第一次窗口内的额外增加值需要循环
                for (int j = 0; j < X; j ++) {
                    if (grumpy[j] == 1) {
                        sum2 = sum2 + customers[j];
                    }
                }
            } else {
                // 移入新增加的元素
                if (grumpy[right] == 1) {
                    sum2 = sum2 + customers[right];
                }
            }
            max = Math.max(max, sum2);
            // 移出当前left的元素
            if (grumpy[left] == 1) {
                sum2 = sum2 - customers[left];
            }
            left ++;
            right = left + X - 1;
        }

        return sum1 + max;


    }

}

基本思路

  1. 既然利用滑动窗口, 就应该使用移入和移出的元素对于窗口的影响来做, 而不是像上面一样考虑数组两端

  2. 总人数可以拆分为 已经确定的客流和窗口X代表的增加客流