zoukankan      html  css  js  c++  java
  • 前缀和以及差分看这一篇就够了

    前缀和以及差分问题:

    导论:

    该博客记录前缀和问题以及差分的解题步骤与相应公式;

    理解其中变化,有不完善的地方慢慢补全;

    如果有错误欢迎指出!

    前缀和:

    首先需要知道前缀和的概念:即数组该位置之前的元素之和。

    还有一个重要的点,在进行前缀和的运算时,下标从1开始,设数组a[0]=0;

    比如a[5] = {0,1,2,3,4};

    求a[1]的前缀和:a[1];

    求a[2]的前缀和:a[1]+a[2];

    ......

    为什么下标要从1 开始:为了方便后面的计算,避免下标转换,设为零,不影响结果

    前缀和的作用: 快速求出元素组中某段区间的和

    一维数组的前缀和问题:

    求数组a中(l,r)区间的和 --->用到前缀和

    1. 需要定义两个数组,第一个为原始数组(a[]),第二个为前缀和数组(s[])

      //初始化原数组
      int[] arr = new int[x];
      for (int i = 1; i <= n; i++) {
      	arr[i] = sc.nextInt();
      }
      
    2. 公式:s[i] = s[i-1]+a[i] {其中s[i]表示a数组的前i项的和}

      //前缀和的计算
      int[] s = new int[x];
      for (int i = 1; i <=n ; i++) {
      	s[i] = s[i-1]+arr[i];
      }
      
    3. 输入区间范围(l,r),s[r]-s[l-1]的结果就是所求区间的和

      sum[r] =a[1]+a[2]+a[3]+a[l-1]+a[l]+a[l+1]......a[r];
      sum[l-1]=a[1]+a[2]+a[3]+a[l-1];
      
      sum[r]-sum[l-1]=a[l]+a[l+1]+......+a[r];
      
      while (m-- !=0){
          int l = sc.nextInt();
          int r = sc.nextInt();
          System.out.println(s[r]-s[l-1]);
      }
      

    二维数组的前缀和问题:

    方法与一维数组大体相同:需要中间数组s[i][j]

    1. 定义两个二维数组,第一个为原始数组a[][],第二个为临时数组b[][]

      // 初始化原始数组
      int[][] arr = new int[n+1][m+1];
      for (int i = 1; i <= n; i++) {
          for (int j = 1; j <=m ; j++) {
          	arr[i][j] = sc.nextInt();
          }
      }
      
    2. 公式:s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + arr[i][j]

      //定义s二维数组,求解前缀和s数组
      int[][] s = new int[n+1][m+1];
      for (int i = 1; i <= n; i++) {
          for (int j = 1; j <=m ; j++) {
              s[i][j] = s[i-1][j]+s[i][j-1]-s[i-1][j-1]+arr[i][j];
          }
      }
      
    3. 输入区间范围(x1,y1,x2,y2),s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]的结果就是所求区间的和;

      //求解前缀和
      while(q-- !=0){
          int x1 = sc.nextInt();
          int y1 = sc.nextInt();
          int x2 = sc.nextInt();
          int y2 = sc.nextInt();
          int res = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
          System.out.println(res);
      }
      

    差分问题:

    首先明白差分的概念:差分其实就是前缀和的逆运算

    差分的作用:如果对某个区间需要每个元素加上C则需要使用差分来减少时间复杂度

    给定a[1],a[2]....a[n];构造擦划分数组b[N];使得a[i] = b[i]+b[2]+....+b[i];

    差分的重点是:构造临时数组b[]

    b[1] = a[1]
    b[2] = a[2] - a[1]
    b[3 ]= a[3] - a[2]
    ...
    b[n] = a[n] - a[n-1]
    

    两个数组:a[],b[],a[]称为b[]的前缀和,b[]称为a[]的差分

    差分的下标也是从1开始;

    核心操作:

    将a[L~R]全部加上C,等价于:b[L] +=C, b[R+1] -=C;

    一维数组的差分问题:

    1. 首先初始化数组s[]

      int[] b = new int[x];
      for (int i = 1; i <=n ; i++) {
          a[i] = sc.nextInt();
      }
      
    2. 按照上面构造数组方式构造b[]数组(两种方法),公式:b[i] = a[i]-a[i-1]

      //构造差分数组
      for (int i = 1; i <=n ; i++) {
          b[i] = a[i]-a[i-1];
      }
      //插入方法:假设a[]中全部为0 则b[]也全部为0,可以执行插入操作
      for(int i = 1; i <= n; i++){
          insert(i,i,a[i])
      }
      

      插入操作:

      public static void insert(int l,int r,int c){
          b[l] +=c;
          b[r+1] -=c;
      }
      
    3. 将所求区间(l,r)在b[]数组划分出来并加上c,公式:b[l] +=c,b[r+1] -=c;

      int l,r,c;
      l = sc.nextInt();
      r = sc.nextInt();
      c = sc.nextInt();
      b[l] +=c;
      b[r+1] -=c;
      

      因为a[]数组是b[]数组的前缀和,b[]是a[]的差分,所以在b[]的某个区间上+c会影响的a区间上的结果

    4. 将差分数组转换成原数组,也就是求差分数组的前缀和,公式:a[i] = a[i-1] +b[i] //类比于s[i]=s[i-1]+a[i]

      for (int i = 1; i <=n ; i++) {
          a[i] = a[i-1]+b[i];
          System.out.print(a[i]+" ");
      }
      

    二维数组的差分问题:

    记住:a[][]数组是b[][]数组的前缀和数组,那么b[][]a[][]的差分数组

    二维差分的核心也是构造差分数组b[][],使得a数组中a[i][j]是b数组左上角(1,1)到右下角(i,j)所包围矩形元素的和;

    怎么让子矩阵中的每个元素加上c;

    先定义一个函数:

    public static void insert(int x1,int y1,int x2,int y2,int c){
        b[x1][y1] += c;
        b[x2+1][y1] -=c;
        b[x1][y2+1] -=c;
        b[x2+1][y2+1] +=c;
    }
    
    1. 初始化原数组a[][]

      for (int i = 1; i <=n; i++) {
          for (int j = 1; j <=m ; j++) {
              a[i][j] = sc.nextInt();
          }
      }
      
    2. 构造差分数组

      初始化B数组从[1][1][i][j]添加元素,就是将a[][]中的元素遍历到B数组中

      int[][] b = new int[x][x];
      for (int i = 1; i <=n; i++) {
          for (int j = 1; j <=m ; j++) {
              insert(i,j,i,j,a[i][j]);
          }
      }
      
    3. 输入矩形中需要+c的范围(x1,y1)(x2,y2),在差分数组b[][]中找到相应的范围+c

      while (q-- != 0){
          int x1,y1,x2,y2;
          x1 = sc.nextInt();
          y1 = sc.nextInt();
          x2 = sc.nextInt();
          y2 = sc.nextInt();
          int c = sc.nextInt();
          insert(x1,y1,x2,y2,c);
      }
      
    4. b[][]数组中的前缀和-->a[][];公式:b[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]

      for (int i = 1; i <=n ; i++) {
          for (int j = 1; j <=m ; j++) {
              b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + b[i][j];
              System.out.print(b[i][j]+" ");
          }
      }
      
    5. 直接输出b[][]中的元素就是a[][]数组中范围所需要+c的结果

    结束:

    感谢各位能看到最后

  • 相关阅读:
    Python六大开源框架对比:Web2py略胜一筹
    软件设计之UML—UML的构成[上]
    Web程序员最常用的11款PHP框架
    PHP常见框架
    WinCE的开发流程
    Windows10如何卸载OneDrive
    Windows系统中环境变量不展开的问题
    线程局部存储空间
    ping pathping tcping psping tracert
    ubuntu ufw 配置
  • 原文地址:https://www.cnblogs.com/xbhog/p/14348830.html
Copyright © 2011-2022 走看看