ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

差分与前缀和

2020-03-01 14:55:14  阅读:270  来源: 互联网

标签:y2 前缀 int 差分 Maxsize y1 x1 dp


1. 一维前缀和 : 

给你一个数列 :a1 , a2 , a3 , a4 , ....... , an 。 以及 k 次询问。 每一次询问给出两个数 L 、R ,要求你回答这个数列的 [ L , R ] 区间内所有数的和是多少 (即 sum( L,R ) )

解法 : 1. 暴力。每次询问都用一层 for 循环求解。复杂度为 O( n*k )

     2.一维前缀和: 另外开一个与存储数列的数组等长度的 数组 dp[N] 。dp[i] 表示 sum(1,i) 。

        之后对于 k 次询问,只需要用 dp[R] - dp[L-1] 表示。

 1 int arr[100+1];
 2 int  dp[100+1];
 3 int n,k,l,r;
 4 cin >> n >> k;
 5 for(int i = 1; i <= n; i++){
 6     cin >> arr[i];
 7     dp[i] = dp[i-1] + arr[i];
 8 }
 9 while(k--){
10     cin >> l >> r;
11     cout<<dp[r] - dp[l-1];
12 }

2.差分(一维):

在上一个问题的基础上,再附加:在 k 次询问以前 , 还有这样一系列操作( 设一共进行了 t 次 ) : 

(1)给定 L 、R  , 对原数列的 [ L , R ] 每一个数增加 p

(2)给定 L 、R ,  对原数列的 [ L , R ]每一个数减少 p

最后再进行之前的k次询问

解法:

新建一个数组 m [ N ] , 引进一个变量 add 来记录实时变化。具体看代码。

 1     for(i = 1;i <= t ; i++){
 2         int L,R,cmd;
 3         cin >> cmd >> L >> R >> p;
 4         if(t == cmd){
 5             m[L]+=p;m[R+1]-=p; 
 6         }
 7         else{
 8             m[L]-=p;m[R+1]+=p;
 9         }
10     }
11     int add=0;
12     for(i=1;i<=n;i++){
13         add+=b[i];
14         dp[i]= dp[i-1]+ arr[i] + add;
15     }

我们假设 t 就等于 1 ( 即只进行了一次修改 )。 方便我们理解差分的工作原理。

假设 N = 10 , 即数列共有10项, 我们现在对区间[ 3,6 ] 的每一个数进行 +1 操作。 这对应于代码的第4行: m[3] += 1  , m[7] -= 1

接下来进入第 11 行, add 初始为 0。

然后进入循环:

* 当1 <= i <= 2 时 , add += 0       dp [i]  = dp[i-1] + arr[i] + add = ap[i-1] + arr[i] + 0    

* 当 i = 3 时 , add += b[3]   -> add = add + 1 = 1

   故 3 <= i <= 6 时 ,dp [i]  = dp[i-1] + arr[i] + add = ap[i-1] + arr[i] + 1  

* 当 i = 7 时 , add += b[7]    -> add = add - 1 = 0

  故 7 <= i  <= 10 时 dp [i]  = dp[i-1] + arr[i] + add = ap[i-1] + arr[i] + 0    

利用差分 , 我们将 t 次区间修改的复杂度从O( t * n )降到了 O( t ) , 而对dp数组的处理复杂度没变。

 

如果题目改成边修改边询问,就是用线段树来求解了。这不属于本次讨论的范畴。

3.二维前缀和:

贴一个网上找到的图 : 图片来源:https://blog.csdn.net/qq_34990731/article/details/82807870 

 

再推荐一个比较精致的博客: 

https://www.cnblogs.com/LMCC1108/p/10753451.html

我们用二维数组 arr[ m ][ n ] 来存储输入所给出的矩阵各个点的数值,并用一个二维数组 dp[ m ][ n ] 来表示 以点 ( 1,1 ) 为左上角 , 点 ( i , j ) 为右下角的矩形区间和。

易得状态转移方程 : dp[ i ][ j ] = dp[ i - 1][ j ] + dp[ i ][ j - 1 ] - dp[ i - 1][ j - 1] + arr[ i ][ j ]

在接下来的询问中 , 每次询问给出 四个数 x1, y1, x2, y2 。 要求你给出包含点 ( x1,y1 ) 、(x2 , y2 )在内的 , 且以( x1, y1 ) 为左上角 , 点( x2 , y2 )为右下角的子矩阵区间和

 1 /* 2D-sum */
 2 #include <iostream>
 3 #define Maxsize 1000+1
 4 using namespace std;
 5 int map[Maxsize][Maxsize];
 6 int dp[Maxsize][Maxsize];
 7 int main(){
 8     /* read */
 9     int n,m;
10     cin >> n >> m;
11     for(int i = 1; i <= n; i++)
12         for(int j = 1; j <= m; j++)
13             cin >> map[i][j];
14 
15     /* init dp array */
16     for(int i = 1; i <= n; i++)
17         for(int j = 1; j <= m; j++)
18             dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + map[i][j];
19 
20     /* query */
21     int q;
22     int x1,y1,x2,y2;
23     /*
24        (x1,y1) -> the upper left  corner
25        (x2,y2) -> the lower right corner
26                                          */
27     cin >> q;
28     while(q--){
29         cin >> x1 >> y1 >> x2 >> y2;
30         x1--;y1--;
31 //      有一点注意,因为画图和定义原因我们发现边
32 //      界好像不对,我们来看看,我们定义的状态是
33 //      整个矩阵包括边的和,而我们要求的也是要包括边的,所以我们要让x1--,y1--
34         cout<<"output: "<<dp[x2][y2] - dp[x1][y2] - dp[x2][y1] + dp[x1][y1]<<endl;
35     }
36     return 0;
37 }

4.差分(二维):  

 

 1 #include <cstdio>
 2 #include <iostream>
 3 #define Maxsize 5+1
 4 using namespace std;
 5 int m,n;
 6 int  map[Maxsize][Maxsize];
 7 int   dp[Maxsize][Maxsize];
 8 int diff[Maxsize][Maxsize];
 9 void init_map(){
10     cin >> m >> n;
11     for(int i = 1; i <= m; i++)
12         for(int j = 1; j <= n; j++)
13             cin >> map[i][j];
14 }
15 void add(int x1,int y1,int x2,int y2,int val){
16     diff[x1][y1] += val;
17     diff[x1][y2+1] -= val;
18     diff[x2+1][y1] -= val;
19     diff[x2+1][y2+1] += val;
20 }
21 void init_dp(){
22     for(int i = 1; i <= m; i++){
23         for(int j = 1; j <= n; j++){
24             diff[i][j] += diff[i-1][j] + diff[i][j-1] - diff[i-1][j-1];
25             dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + map[i][j] + diff[i][j];
26         }
27     }
28 }
29 void query(int x1,int y1,int x2,int y2){
30     x1--;y1--;
31     cout << dp[x2][y2] - dp[x2][y1] - dp[x1][y2] + dp[x1][y1] << endl;
32 }
33 int main(){
34     init_map();
35     int k,t;
36     int x1,y1,x2,y2,val;
37     cin >> k;
38     while (k--) {
39         cin >> x1 >> y1 >> x2 >> y2 >> val;
40         add(x1,y1,x2,y2,val);
41     }
42     init_dp();
43     cin >> t;
44     while (t--) {
45         cin >> x1 >> y1 >> x2 >> y2;
46         query(x1,y1,x2,y2);
47     }
48     return 0;
49 }

5.利用 hash 将二维前缀和问题降维处理:

当题目中给的矩阵过大的时候,如果强行开二维数组存矩阵就会MLE , 此时如果用 hash 就可以减少空间。

例: HDU 6514 Monitor https://vjudge.net/problem/HDU-6514

 

标签:y2,前缀,int,差分,Maxsize,y1,x1,dp
来源: https://www.cnblogs.com/popodynasty/p/12389798.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有