差分

差分基本概念及其性质   :可以看看这个视频  b站up 星垂月朦胧

给定一个数组 a ,其中 a = [a1, a2, ..., an],我们定义差分数组 d 如下:

d[i] = a[i] - a[i-1]

其中,d[1] = a[1]。这个差分数组 d记录了原数组 a 相邻元素之间的差值(ai+1-ai)。通过计算差分数组的前缀和,我们可以还原原数组 a。

例如,给定数组 a = [1, 3, 7, 5, 2],我们可以计算其差分数组 d 如下:

d = [1, 2, 4, -2, -3]

通过对差分数组 d 计算前缀和,可以还原原数组 a:

s[d] = [1, 3, 7, 5, 2]

差分数组的一个重要性质是可以用来简化区间操作。例如,对于区间 [L, R] 上的所有元素加上一个值 v,可以等价为两个点的操作:

d[L] += v 
d[R+1] -= v

这个操作表示从位置 L 开始增加 v,并从位置 R+1 开始减少 v,从而实现对整个区间 [L, R] 的更新。

 模板一

package com.coedes.difference.template;

import java.util.Arrays;

/**
 * @description:from https://www.bilibili.com/video/BV1pi4y1j7si?p=2&vd_source=4accc3f927b8b6579f872125bad11046
 * @author: wenLiu
 * @create: 2024/4/19 23:00
 */
public class Template1 {
    static int[] d = new int[6]; // 初始化长度默认n+1防止d[r+1]操作溢出,默认元素值为0

    // 定义add函数
    static void add(int l, int r, int v) {
        d[l] += v;
        //初始化长度默认n+1 防止d[r+1]操作溢出无需特判
            /*if (r + 1 < d.length) {
                d[r + 1] -= v;
            }*/
        d[r + 1] -= v;
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 7, 5, 2};

        add(2, 4, 5);
        add(1, 3, 2);
        add(0, 2, -3);

        // 对d数组进行前缀和处理
        for (int i = 1; i < d.length; i++) {
            d[i] += d[i - 1];
        }

        // 输出处理后的arr数组
        for (int i = 0; i < arr.length; i++) {
            arr[i] += d[i];
        }

        // 打印结果
        for (int num : arr) {
            System.out.print(num + " ");
        }

        Arrays.fill(d, 0);
    }
}

模板二:

import java.util.Scanner;
public class Template2 {
    int[] b = new int[100010];
    public  void insert(int l ,int r ,int c){
        b[l] += c;
        b[r+1] -= c;
    }
    public static void main(String[] args){
        new Template2().test();
    }
    public void test(){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int m = scan.nextInt();
        int[] a = new int[n+1];

        for(int i = 1 ; i <= n ; i ++ ){
            a[i] = scan.nextInt();
        }
        //b[1] = b[1]+a[1] = 0 + a[1] = a[1] ,  b[2] = b[2]-a[1] = 0 - a[1] = -a[1]
        //b[2] = b[2]+a[2] = a[2]-a[1] ,  b[3] = b[3]-a[2] = -a[2]....
     //insert(i,i,a[i]) 初始化差分数组 for(int i = 1;i <= n ; i ++ ){ insert(i,i,a[i]); } while(m-->0){ int l = scan.nextInt(); int r = scan.nextInt(); int c = scan.nextInt(); insert(l,r,c); } for(int i = 1;i <= n ; i ++ ){ a[i] = a[i-1] + b[i]; // a[i]-a[i-1] = b[i] => a[i] = a[i-1]+b[i] System.out.print(a[i] + " "); } } }

LeetCode 1094. 拼车

 参考  该题解可以对差分基本概念有个了解。

class Solution {

    static int[] d;

    static void insert(int v, int l, int r) {
        d[l] += v;
        d[r] -= v;
    }
public boolean carPooling(int[][] trips, int capacity) { int maxTo = Integer.MIN_VALUE; for (int i = 0; i < trips.length; i++) maxTo = Math.max(maxTo, trips[i][2]); d = new int[maxTo+1]; for (int[] trip : trips) { insert(trip[0], trip[1], trip[2]); } int pre = 0; for (int i = 0; i <= maxTo; i++) { pre += d[i]; if (pre > capacity) { return false; } } return true; } }
class Solution {
    TreeMap<Integer, Integer> map; // TreeMap 代替d数组 避免下标考虑

    static void insert(TreeMap<Integer, Integer> map, int v, int l, int r) {
        map.put(l, map.getOrDefault(l, 0) + v);
        map.put(r, map.getOrDefault(r, 0) - v);
    }

    public boolean carPooling(int[][] trips, int capacity) {
        map = new TreeMap<>();

        for (int[] trip : trips) {
            insert(map, trip[0], trip[1], trip[2]);
        }

        int pre = 0;
        for (int key : map.keySet()) {
            int value = map.get(key);
            pre += value;
            if (pre > capacity) {
                return false;
            }
        }

        return true;
    }
}

模板法:

class Solution {
    static int[] d;

    static void insert(int l, int r, int v) {
        d[l] += v;
        d[r + 1] -= v;
    }

    public int[] corpFlightBookings(int[][] bookings, int n) {
        d = new int[n + 2];
        for (int[] booking : bookings) {
            insert(booking[0], booking[1], booking[2]);
        }
        int[] res = new int[n];
        int preSum = 0;
        for (int i = 0; i < res.length; i++) {
            preSum += d[i+1];
            res[i] = preSum;
        }
        return res;
    }
}
public class Solution2 { // 压缩差分数组d
    public int[] corpFlightBookings(int[][] bookings, int n) {
        //因为原始数组为0 没必要另外开辟数组存放差分数组d
        int[] nums = new int[n];
        for (int[] booking : bookings) {
            // 注意:这里要减1,因为nums数组的下标是从0开始的
            // l = i-1   r+1 = i
            nums[booking[0] - 1] += booking[2];
            if (booking[1] < n) {
                nums[booking[1]] -= booking[2];
            }
        }
        for (int i = 1; i < n; i++) {
            nums[i] += nums[i - 1];
        }
        return nums;
    }
}

描述

1. 给定一个长度为 n 的只含小写字母的字符串。

2. 定义轮换操作 k,将字符串中下标属于[1,k]的每个小写字母向后移动一位,即 a -> b, b -> c, ..., z -> a。

3. 给出一个操作序列 k1, k2, ...,km表示连续对字符串进行轮换操作。

4. 求经过所有操作序列中的操作后,字符串的最终结果。

输入

第一行两个整数n,m(1 ≤ n,m ≤105)代表字符串长度以及轮换序列的长度
第二行为字符串s的初始状态
第三行有m个整数k(1 ≤ k i≤ n),代表操作序列里的每一次操作,以空格隔开。

输出

一行字符串,代表最终结果

样例

input

5 3
abcde
3 5 2

output

deeef

   

package com.coedes.difference.visa2023;

import java.util.Scanner;

/**
 * @description:from https://codefun2000.com/p/P1135
 * @author: wenLiu
 * @create: 2024/4/20 12:46
 */
public class Main {
    static int[] d;
    static void insert(int l, int r, int v) {
        d[l] += v;
        d[r + 1] -= v;
    }

    public static void main(String[] args) {
        /**
         * 5 3
         * abcde
         * 3 5 2
         */
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        String s = in.next();

        // 初始化差分数组
        d = new int[n + 2];
        char[] chars = new char[n + 1];
        for (int i = 0; i < s.length(); i++) {
            chars[i+1] = s.charAt(i);
        }

        int k;
        // 计算区间操作后的差分数组
        for (int i = 0; i < m; i++) {
            k = in.nextInt();
            calDifference(k);
        }

        //  求差分数组前缀和
        for (int i = 1; i <= n; i++) {
            d[i] += d[i - 1];
        }


        // 输出结果
        for (int i = 1 ; i < chars.length; i++) {
            int u = chars[i] - 'a';
            u = (u + d[i]) % 26;
            System.out.print((char) ('a' + u));
        }

    }
    private static void calDifference(int k) {
        insert(1, k, 1);
    }
}

  

    • 监考规则:
      • 只监视一个考场:每分钟收费3金币。
      • 同时监视两个以上的考场:每分钟收费4金币。
      • 当处于两个监考任务之间的空隙之间时,会进入待机状态,每分钟消耗1金币
    • 输入数据:
      • n:考场数量。1 ≤n ≤ 10000
      • 每个考场的开始时间和结束时间。0 ≤ ai,bi ≤ 106
    • 输出:
      • 今天的监考任务所收取的金币总额。

样例1

输入

3
1 5
4 6
6 6

输出

21

思路:差分

某一时刻收取的金币值,是由当前时刻监考的考场数量决定的,题目给定每个考场的开始时间a和结束时间b,相当于对区间[a,b]执行+1操作

package com.coedes.difference.huawei2023041901;


import java.util.Scanner;

/**
 * @description:https://codefun2000.com/p/P1195
 * @author: wenLiu
 * @create: 2024/4/20 13:52
 */
public class Main {
    // 0 ≤ ai, bi ≤ 10e6
    private static final int N = 1000010;
    //差分数组
    private static int[] d;

    public static void main(String[] args) {
        initArrays();
        long res = getRes();
        System.out.println(res);
    }

    private static long getRes() {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int minL = Integer.MAX_VALUE;
        int maxR = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            int l1 = in.nextInt();
            int r1 = in.nextInt();
            // 差分数组
            d[l1] += 1;
            d[r1 + 1] -= 1;
            minL = Math.min(l1, minL);
            maxR = Math.max(r1, maxR);
        }
        long pre = 0;
        long res = 0;
        for (int i = minL; i <= maxR; i++) {
            pre += d[i];//当前时间段同时存在的考试场数
            if (pre == 0) {
                res++;
            } else if (pre == 1) {
                res += 3;
            } else {
                res += 4;
            }
        }
        return res;
    }

    private static void initArrays() {
        d = new int[N];
    }
}

2023美团-天文爱好者

流星出现时刻为s,消失时刻为t,如果在时间段[s, t]内,就能观测到这颗流星。

希望找到一个最佳观测时刻,观测到最多的流星数量。

同时,他想知道在这个最佳时刻,他最多能观测到多少个流星,以及有多少个这样的最佳时刻?

1 ≤n< 100000,1 <si<t¡< 109

package com.coedes.difference.meituan2023031102;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

/**
 * @description:https://codefun2000.com/p/P1078
 * @author: wenLiu
 * @create: 2024/4/20 15:31
 */
public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());
        int[] l = new int[n];
        int[] r = new int[n];

        String[] lInput = br.readLine().split(" ");
        String[] rInput = br.readLine().split(" ");

        // 读取 l 数组
        for (int i = 0; i < n; i++) {
            l[i] = Integer.parseInt(lInput[i]);
        }

        // 读取 r 数组
        for (int i = 0; i < n; i++) {
            r[i] = Integer.parseInt(rInput[i]);
        }

        // 构建差分数组
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for (int i = 0; i < n; i++) {
            map.merge(l[i], 1, Integer::sum);  // d[l]+=1
            map.merge(r[i] + 1, -1, Integer::sum);  // d[r+1]-=1
        }

        // 统计每个时间点出现流星频数 {流星数量, 出现对应流星数量天数}
        TreeMap<Integer, Integer> cnts = new TreeMap<>();
        int sum = 0;
        int beg = map.firstKey();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int key = entry.getKey();
            int value = entry.getValue();

            cnts.put(sum, cnts.getOrDefault(sum, 0) + (key - beg));
            beg = key;
            sum += value;
        }

        // 找到最大的 cnts 中的值
        int maxCnt = 0;
        int maxNum = 0;
        for (Map.Entry<Integer, Integer> entry : cnts.entrySet()) {
            int u = entry.getKey();
            int v = entry.getValue();
            if (u > maxCnt) {
                maxCnt = u;
                maxNum = v;
            }
        }

        System.out.println(maxCnt + " " + maxNum);
    }
}

  

 
posted @ 2024-04-20 19:42  菠萝包与冰美式  阅读(9)  评论(0编辑  收藏  举报