C++前缀和与差分算法的示例分析


这篇文章将为大家详细讲解有关C++前缀和与差分算法的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。先来了解这样一个问题:输入一个长度为n的整数序列。接下来再输入m个询问,每个询问输入一对l, r。对于每个询问,输出原序列中从第l个数到第r个数的和。我们很容易想出暴力解法,遍历区间求和。代码如下:这样的时间复杂度为O(n*m),如果nm数据量稍微大一点就有可能超时,而我们如果使用前缀和的方法来做的话就能够将时间复杂度降到O(n+m),大大提高了运算效率。具体做法:首先做一个预处理,定义一个sum[]数组,sum[i]代表a数组中前i个数的和。求前缀和运算:然后查询操作:对于每次查询,只需执行sum[r]-sum[l-1] ,时间复杂度为O(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];图解这样,对于每个询问,只需要执行 sum[r]-sum[l-1]。输出原序列中从第l个数到第r个数的和的时间复杂度变成了O(1)。我们把它叫做一维前缀和。总结:练习一道题目输入一个长度为n的整数序列。
接下来再输入m个询问,每个询问输入一对l, r。
对于每个询问,输出原序列中从第l个数到第r个数的和。输入格式第一行包含两个整数n和m。
第二行包含n个整数,表示整数数列。
接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。输出格式共m行,每行输出一个询问的结果。数据范围1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000输入样例:5 3
2 1 3 6 4
1 2
1 3
2 4输出样例:3
6
10代码:如果数组变成了二维数组怎么办呢?先给出问题:输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。同一维前缀和一样,我们先来定义一个二维数组s[][], s[i][j]表示二维数组中,左上角(1,1)到右下角( i,j )所包围的矩阵元素的和。接下来推导二维前缀和的公式。先看一张图:紫色面积是指(1,1)左上角到(i,j-1)右下角的矩形面积, 绿色面积是指(1,1)左上角到(i-1, j )右下角的矩形面积。每一个颜色的矩形面积都代表了它所包围元素的和。从图中我们很容易看出,整个外围蓝色矩形面积s[i][j] = 绿色面积s[i-1][j] + 紫色面积s[i][j-1] – 重复加的红色的面积s[i-1][j-1]+小方块的面积a[i][j];因此得出二维前缀和预处理公式s[i] [j] = s[i-1][j] + s[i][j-1 ] + a[i] [j] – s[i-1][ j-1]接下来回归问题去求以(x1,y1)为左上角和以(x2,y2)为右下角的矩阵的元素的和。如图:紫色面积是指 ( 1,1 )左上角到(x1-1,y2)右下角的矩形面积 ,黄色面积是指(1,1)左上角到(x2,y1-1)右下角的矩形面积;不难推出:绿色矩形的面积 = 整个外围面积s[x2, y2] – 黄色面积s[x2, y1 - 1] – 紫色面积s[x1 - 1, y2] + 重复减去的红色面积 s[x1 - 1, y1 - 1]因此二维前缀和的结论为:以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:s[x2, y2] – s[x1 – 1, y2] – s[x2, y1 – 1] + s[x1 – 1, y1 – 1]总结:练习一道完整题目:
输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1, y1, x2, y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。输入格式第一行包含三个整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含四个整数x1, y1, x2, y2,表示一组询问。输出格式共q行,每行输出一个询问的结果。数据范围1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000输入样例:3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4输出样例:17
27
21代码:类似于数学中的求导和积分,差分可以看成前缀和的逆运算。差分数组:首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];然后我们构造一个数组b : b[1] ,b[2] , b[3],,,,,, b[i];使得 a[i] = b[1] + b[2 ]+ b[3] +,,,,,, + b[i]也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。考虑如何构造差分b数组?最为直接的方法如下:a[0 ]= 0;b[1] = a[1] – a[0];b[2] = a[2] – a[1];b[3] =a [3] – a[2];……..b[n] = a[n] – a[n-1];图示免费云主机域名:我们只要有b数组,通过前缀和运算,就可以在O(n) 的时间内得到a数组 。知道了差分数组有什么用呢? 别着急,慢慢往下看。话说有这么一个问题:给定区间[l ,r ],让我们把a数组中的[ l, r]区间中的每一个数都加上c,即 a[l] + c , a[l+1] + c , a[l+2] + c ,,,,,, a[r] + c;暴力做法是for循环lr区间,时间复杂度O(n),如果我们需要对原数组执行m次这样的操作,时间复杂度就会变成O(n*m)。有没有更高效的做法吗? 考虑差分做法,(差分数组派上用场了)。始终要记得,a数组是b数组的前缀和数组,比如对b数组的b[i]的修改,会影响到a数组中从a[i]及往后的每一个数。首先让差分b数组中的 b[l] + c ,通过前缀和运算,a数组变成 a[l] + c ,a[l+1] + c,,,,,, a[n] + c;然后我们打个补丁,b[r+1] - c, 通过前缀和运算,a数组变成 a[r+1] – c,a[r+2] – c,,,,,,,a[n] – c;为啥还要打个补丁?我们画个图理解一下这个公式的由来:b[l] + c,效果使得a数组中 a[l]及以后的数都加上了c(红色部分),但我们只要求lr区间加上c, 因此还需要执行 b[r+1] - c,让a数组中a[r+1]及往后的区间再减去c(绿色部分),这样对于a[r] 以后区间的数相当于没有发生改变。因此我们得出一维差分结论:给a数组中的[ l, r]区间中的每一个数都加上c,只需对差分数组bb[l] + = c, b[r+1] - = c。时间复杂度为O(1), 大大提高了效率。总结:题目练习: AcWing 797. 差分输入一个长度为n的整数序列。
接下来输入m个操作,每个操作包含三个整数l, r, c,表示将序列中[l, r]之间的每个数加上c。
请你输出进行完所有操作后的序列。输入格式第一行包含两个整数n和m。
第二行包含n个整数,表示整数序列。
接下来m行,每行包含三个整数l,r,c,表示一个操作。输出格式共一行,包含n个整数,表示最终序列。数据范围1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000输入样例:6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1输出样例:3 4 5 3 4 2
AC代码如果扩展到二维,我们需要让二维数组被选中的子矩阵中的每个元素的值加上c,是否也可以达到O(1)的时间复杂度。答案是可以的,考虑二维差分。a[][]数组是b[][]数组的前缀和数组,那么b[][]a[][]的差分数组原数组: a[i][j]我们去构造差分数组: b[i][j]使得a数组中a[i][j]b数组左上角(1,1)到右下角(i,j)所包围矩形元素的和。如何构造b数组呢?其实关于差分数组,我们并不用考虑其构造方法,因为我们使用差分操作在对原数组进行修改的过程中,实际上就可以构造出差分数组。同一维差分,我们构造二维差分数组目的是为了 让原二维数组a中所选中子矩阵中的每一个元素加上c的操作,可以由O(n*n)的时间复杂度优化成O(1)已知原数组a中被选中的子矩阵为 以(x1,y1)为左上角,以(x2,y2)为右上角所围成的矩形区域;始终要记得,a数组是b数组的前缀和数组,比如对b数组的b[i][j]的修改,会影响到a数组中从a[i][j]及往后的每一个数。假定我们已经构造好了b数组,类比一维差分,我们执行以下操作
来使被选中的子矩阵中的每个元素的值加上cb[x1][y1] + = c;b[x1,][y2+1] – = c;b[x2+1][y1] – = c;b[x2+1][y2+1] + = c;每次对b数组执行以上操作,等价于:我们画个图去理解一下这个过程:b[x1][ y1 ] +=c ; 对应图1 ,让整个a数组中蓝色矩形面积的元素都加上了c
b[x1,][y2+1]-=c ; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c,使其内元素不发生改变。
b[x2+1][y1]- =c ; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c,使其内元素不发生改变。
b[x2+1][y2+1]+=c; 对应图4,让整个a数组中红色矩形面积的元素再加上c,红色内的相当于被减了两次,再加上一次c,才能使其恢复。我们将上述操作封装成一个插入函数:我们可以先假想a数组为空,那么b数组一开始也为空,但是实际上a数组并不为空,因此我们每次让以(i,j)为左上角到以(i,j)为右上角面积内元素(其实就是一个小方格的面积)去插入 c=a[i][j],等价于原数组a(i,j)(i,j)范围内 加上了 a[i][j] ,因此执行n*m次插入操作,就成功构建了差分b数组.这叫做曲线救国。代码如下:当然关于二维差分操作也有直接的构造方法,公式如下:b[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]二维差分数组的构造同一维差分思维相同,因次在这里就不再展开叙述了。总结:题目练习: AcWing 798. 差分矩阵输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。输入格式第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。输出格式共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000输入样例:3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1输出样例:2 3 4 1
4 3 4 1
2 2 2 2AC代码:关于“C++前缀和与差分算法的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

相关推荐: javascript有哪些基本特点

本文小编为大家详细介绍“javascript有哪些基本特点”,内容详细,步骤清晰,细节处理妥当,希望这篇“javascript有哪些基本特点”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。   JavaScript是一种基于对象(Ob…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 10/03 10:01
下一篇 10/03 10:01

相关推荐