路由器, 美团笔试题, 差分

1. 前缀和与差分及其应用
有两个数组A和B。

前缀和数组A:

\(a_1 , a_2, a_3..., a_n\)

差分数组B:

\(b_1, b_2, b_3, ..., b_n\)

数组A和B的关系如下:

(1)前缀和数组A:

\(a_1 , a_2, a_3..., a_n\)又等于

\(b_1, b_1+b_2, b_1+b_2+b_3, ..., b_1+b_2+...+bn\)

(2)差分数组B:

\(b_1, b_2, b_3, ..., b_n\) 等于

\(a_1, a_2 - a_1, a_3 - a_2, ..., a_n - a_{n-1}\)

前缀和和差分互为逆运算。因此,数组A是数组B的前缀和数组,数组B是数组A的差分数组。

差分数组的作用:

我们需要在A数组的某个区间【l,r】上(a[l], a[l+1], ..., a[r])频繁加上或者减去某个数c,一次操作需要对A数组操作r-l+1次,如果频繁操作,时间复杂度较高。但如果在数组A的差分数组上操作只需要\(b_l+c\), \(b_{r+1}-c\),再通过数组B重构出A数组,就可以达到在原A数组的区间【l,r】上加上c。简言之,求 a数组【l, r】区间所有数加一个数c, 则只需要 \(b_l + c\), 和 \(b_{r+1} - c\),在通过B数组重构A数组,即可以在a数组【l, r】区间所有数加一个数c。

分析:

\(b_l+c\)会影响到\(a_l, a_{l+1}, a_n\),他们是b数组的前缀和,即\(a_l, a_{l+1}, a_n\)变成了\(a_l+c, a_{l+1}+c, a_n+c\)

\(a_{r+1}, a_{r+2}, a_n\)同理, \(b_{r+1}+c\)会影响到\(a_{r+1}, a_{r+2}, a_n\),他们是数组B的前缀和,即变成\(a_{r+1}-c, a_{r+2}-c, a_n-c\)了。

因此, \(b_l + c\), 和 \(b_{r+1} - c\)会使得即\(a_l, a_{l+1}, a_r\)变成了\(a_l+c, a_{l+1}+c, a_r+c\)

如果1 <= l, r, <= n,我们由k(1 <= k <= n)次这样的操作,如果在数组A上直接操作,时间复杂度为\(O(n^2)\), 如果通过构造A的差分数组,每次操作为O(1),K次操作为O(n),最后再通过数组B重新构造出新数组A。

2. 例题: 路由器, 美团笔试题

import java.util.*;
public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), k = sc.nextInt();
        int[] a = new int[n];
        for(int i=0; i < n; i++) 
            a[i] = sc.nextInt();
        int[] b = new int[n]; //前缀和数组
        int[] c = new int[n]; // 差分数组
        for(int i=0; i < n; i++) {
            int l = Math.max(i-a[i], 0);
            int r = Math.min(i+a[i], n-1);
            c[l] += 1;
            if(r+1 < n) c[r+1] -= 1;
        }
        int res = 0;
        // 通过C数组重构B数组
        for(int i=0; i < n; i++) {
            if(i > 0)
                b[i] = c[i] + b[i-1];
            else
                b[0] = c[0];
            if(b[i] >= k) res++;
        }
        System.out.println(res);
    }
}
posted @ 2020-06-23 20:56  li修远  阅读(164)  评论(0编辑  收藏  举报