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

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);
    }
}
原文地址:https://www.cnblogs.com/lixyuan/p/13184460.html