前缀和与差分
一维前缀和:
定义:
前缀和是指某序列的前n项和
公式:
输入:
s[i] = arr[0] i = 0
s[i]=s[i−1]+arr[i] i > 0
输出:
[left - right];
s[right] - s [left - 1];
应用场景:
可以快速的查询某一个区间的和
Code
#include <stdio.h>
int main()
{
int total;
int left , right ; //求left - right 范围的和
scanf("%d",&total);
int arr[total] = {0};
int sum[total] = {0};
for( int i = 0 ; i < total ; i++ ) //数据写入数组,原数组arr
{
scanf("%d",&arr[i]);
}
//求前缀和
for( int i = 0 ; i < total ; i++ )
{
if( i == 0 )
{
sum[i] = arr[i]; // 0下标等于0
}
else
{
sum[i] = sum[i-1] + arr[i]; //前缀和
}
}
scanf("%d%d",&left,&right);
// [left right]的和
printf("%d" , sum[right] - sum[left - 1] );
return 0;
}
二维前缀和
定义:
矩阵的和
公式:
求sum:
s [ i ] [ j ] = s [ i ] [ j − 1 ] + s [ i − 1 ] [ j ] − s [ i − 1 ] [ j − 1 ] + a [ i ] [ j ]
输出:
sum[x2 ] [y2] - sum[ x2 ] [y1-1] - sum[ x1-1 ] [ y2] + sum[x1-1] [y1-1]
应用场景:
对于二维矩阵可以快速的求解某一个矩阵的面积
code
#include <Stdio.h>
int n = 3 , m = 4;
int arr[n][m] = { {1 , 5 , 6 , 8 }
{9 , 6 , 7 , 3 }
{5 , 3 , 2 , 4}
};
int sum[m][n];
void pre_sum()
{
//初始化开始
sum[0][0] = arr[0][0]; //0特殊位置 初始化
//竖着的初始化
for(int i = 1 ; i < n ; i++ )
{
sum[i][0] = sum[i-1][0] + arr[i][0];
}
//横着的初始化
for(int j = 1 ; j < m ; j++ )
{
sum[0][j] = sum[0][j-1] + arr[0][j];
}
for(int i = 1 ; i < n ; i++ )
{
for(int j = 1 ; j < m ; j++ )
{
sum[i][j] = sum[i-1][j] + sum[i][j-1] + arr[i][j] - sum[i-1][j-1];
}
}
//初始化结束
}
int getsum(int x1 ,int y1, int x2 ,int y2)
{
//情况1: x1,y1在(0,0),矩阵很舒服,直接返回就ok
if( x1 == 0 && x2 == 0 )
{
return sum[x2][y2];
}
//情况2:x1 为 0 ,左上角行为0 抽象为多行的一维前缀和
if( x1 == 0 )
{
return sum[x2][y2] - sum[x2][y1-1];
}
//情况3:y1 为 0 ,左上角列为0 ,抽象为多列的一维前缀和
if( y1 == 0 )
{
return sum[x2][y2] - sum[x1-1][y2];
}
return sum[x2][y2] - sum[x2][y1-1] - sum[x1-1][y2] + sum[x1-1][y1-1];
}
int main()
{
//int n , m; //数组规模
//scanf("%d%d",&n,&m);
//初始化的操作和二维动态规划很像!
int x1 ,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
pre_sum();
int ans = getsum(x1,y2,x2,y2);
printf("%d",ans);
return 0;
}
差分
定义:
差分就是将数列中的每一项分别与前一项数做差
一般认为:差分是前缀和的逆运算
差分序列第一个数和原来的第一个数一样
开数组的时候,要比原来的 arr 数组多开一个大小
性质
差分序列求前缀和可得原序列
公式
求差分数组:
只需要对范围[left , right ]
d[left] += val;
d[right + 1 ] -= val;
使差分数组在left, right范围内实现+ val ;
差分求前缀和:
a[i]=a[i]+a[i−1]
差分求前缀和为原数组
应用场景
当求序列的某个区间 + / - val 时
Code
#include <Stdio.h>
void add(int left ,int right , int val)
{
d[left] += val;
d[right + 1] -= val;
}
int main()
{
int total , line;
scanf("%d%d",&total,&line);
for(int i = 0 ; i < total ; i++ )
{
scanf("%d",&arr[i]);
}
int left , right,val;
//假设1次
scanf("%d%d%d",&left,&right,&val);
add(left,right,val);
//差分求前缀和
for(int i = 0 ; i < total; i++ )
{
d[i] += d[i-1];
}
//原序列和差分合并完成 + / - val
for(int i = 0 ; i < total ; i++ )
{
arr[i] += d[i];
printf("%d",arr[i]);
}
return 0;
}
对于下面这行
arr[i] += d[i];
因为我们差分数组开的时全是0 , 而不是原数组复制一遍
所以,我们最后要写回去!