一维前缀和
AcWing 795. 前缀和
【题目描述】
【思路】
一维前缀和
S[i] = a[1] + a[2] + … a[i]
a[l] + … + a[r] = S[r] - S[l - 1] = S[r]-S[l]+a[l]
import java.util.*;
import java.io.*;
class Main{
static int N = 100010;
static int preSum [] = new int[N];
static int f[] = new int[N];
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String arg [] = bf.readLine().split(" "); //读入n 和 q
int n = Integer.parseInt(arg[0]), q = Integer.parseInt(arg[1]);
String data[] = bf.readLine().split(" ");//读入序列
for(int i = 1; i <= n; i ++) f[i] = Integer.parseInt(data[i - 1]);
for(int i = 1; i <= n; i++) preSum[i] = preSum[i - 1] + f[i]; //前缀和数组
while( q -- > 0){
String s[] = bf.readLine().split(" ");
int l = Integer.parseInt(s[0]), r = Integer.parseInt(s[1]);
System.out.println(preSum[r] - preSum[l - 1]);
}
}
}
二维前缀和
AcWing 796. 子矩阵的和
【题目描述】
AcWing 796. 子矩阵的和
【思路】
二维前缀和
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
import java.io.*;
public class Main{
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s[] = bf.readLine().split(" ");
int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]), k =Integer.parseInt(s[2]);
int [][]q = new int[n+1][m+1];
int [][]sum = new int[n+1][m+1];
for(int i = 1; i <= n; i++){
String strArr[] = bf.readLine().split(" ");
for (int j = 1; j <= m; j++) q[i][j] = Integer.parseInt(strArr[j-1]);
}
//用来暂时存放每一行的前缀和
int col [] = new int[m+1];
//前缀和二维数组
for(int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
col[j] = col[j - 1] + q[i][j];
sum[i][j] = sum[i-1][j] + col[j];
//System.out.print(sum[i][j] + " ");
}
//System.out.println();
}
while( k > 0){
String p[] = bf.readLine().split(" ");
int x1 = Integer.parseInt(p[0]), y1 = Integer.parseInt(p[1]), x2 =Integer.parseInt(p[2]), y2 = Integer.parseInt(p[3]);
//画个图就知道下面公式的意义了
int res = sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 -1];
System.out.println(res);
k --;
}
}
}
import java.util.Scanner;
class Main{
static int N = 1010;
static int f[][] = new int[N][N]; //原数据
static int preSum[][] = new int[N][N]; //前缀和数组
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
int n = reader.nextInt(), m = reader.nextInt(), q = reader.nextInt();
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
f[i][j] = reader.nextInt();
//计算前缀和数组: preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + f[i][j];
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + f[i][j];
while( q-- > 0){
int x1 = reader.nextInt(), y1 = reader.nextInt(), x2 = reader.nextInt(), y2 = reader.nextInt();
System.out.println(preSum[x2][y2] - preSum[x2][y1 - 1] - preSum[x1 - 1][y2] + preSum[x1 - 1][ y1 - 1]);
}
}
}
一维差分
ACWing 797. 差分
【题目描述】
【思路】
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
对于数组a[i] = b[1] + b[2 ]+ b[3] +…… + b[i]
则a数组是b数组的前缀和数组,b数组叫做a数组的差分数组。差分数组为:
b[0] = 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数组。给定数组a,对a数组[l,r]区间中的每一个数加上c,常规做法是循环l到r加上c。执行m次操作,时间复杂度就为O(n*m)。
对于这个问题,就可以使用差分数组了:
给a数组中的[ l, r]区间中的每一个数都加上c,只需对差分数组b做 b[l] + = c, b[r+1] - = c。时间复杂度为O(1)。
import java.io.*;
class Main{
static int N = 100010;
static int [] b = new int[N];
static int [] a = new int[N];
//在原序列b [l,r]区间执行加c操作
public static void insert(int l, int r, int c){
b[l] += c;
b[r+1] -= c;
}
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s [] = bf.readLine().split(" ");
int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
String strArr[] = bf.readLine().split(" ");
for(int i = 1; i <= n; i++) a[i] = Integer.parseInt(strArr[i - 1]);
//构造差分数组
for(int i = 1; i <= n; i++) b[i] = a[i] - a[i - 1];
while(m > 0){
String p[] = bf.readLine().split(" ");
int l = Integer.parseInt(p[0]), r = Integer.parseInt(p[1]), c = Integer.parseInt(p[2]);
insert(l, r, c);
m--;
}
for( int i = 1; i <= n; i++) {
a[i] = a[i-1] + b[i]; //前缀和运算
System.out.print(a[i]+" ");
}
}
}
二维差分
AcWing 798. 差分矩阵
【题目描述】
【思路】
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c
import java.io.*;
public class Main{
static int N = 1010;
static int [][] b = new int[N][N];
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;
}
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
String arg[]= bf.readLine().split(" ");
int n = Integer.parseInt(arg[0]), m = Integer.parseInt(arg[1]), q = Integer.parseInt(arg[2]);
int [][] a= new int[N][N];
for(int i = 1; i <= n; i++){
String s[] = bf.readLine().split(" ");
for(int j = 1; j <= m; j++) a[i][j] = Integer.parseInt(s[j - 1]);
}
//构建差分矩阵
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++) insert(i, j, i, j, a[i][j]);
}
while( q > 0){
String s[]= bf.readLine().split(" ");
int x1 = Integer.parseInt(s[0]), y1 = Integer.parseInt(s[1]), x2 = Integer.parseInt(s[2]),
y2 = Integer.parseInt(s[3]), c = Integer.parseInt(s[4]);
insert(x1, y1, x2, y2, c);
q--;
}
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];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){//二维前缀和
log.write(b[i][j] +" ");
}
log.write("\n");
}
log.flush();
bf.close();
log.close();
}
}
/*
000
000
000
110
110
000
110
101
011
001
010
100
*/
import java.util.*;
import java.io.*;
class Main{
static int N = 2010;
static int a[][] = new int [N][N];
static int b[][] = new int [N][N];
public static void insert(int x1, int y1, int x2, int y2, int c){
b[x1][y1] += c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y1] -= c;
b[x2 + 1][y2 + 1] += c;
}
public static void main(String args[]) throws Exception{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
String []s = bf.readLine().split(" ");
int n = Integer.parseInt(s[0]), m = Integer.parseInt(s[1]);
// 构造差分矩阵
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
}
}
//与前缀和数组一样都是0
while(m -- > 0){
String []p = bf.readLine().split(" ");
int x1 = Integer.parseInt(p[0]), y1 = Integer.parseInt(p[1]);
int x2 = Integer.parseInt(p[2]), y2 = Integer.parseInt(p[3]);
insert(x1, y1, x2, y2, 1);
}
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j]; // 记录的是操作的次数,最后奇数输出1,偶数输出0即可
int num = a[i][j]&1;
log.write(num+"");
}
log.write("\n");
}
// 刷缓存,才会有输出
log.flush();
// 关闭输入输出流
bf.close();
log.close();
}
}
题目要求同时将一段子数组全部加上1或者减去1, 直觉上就考虑采用差分数组的思想, 将原数组每一项的原始温度减去目标温度, 得到每一项需要改变的数值, 然后求出该数组的差分数组, 目标是使得差分数组的每一项变成0, 我们的操作方式有2种:
选择任意两项, 一项加1, 另一项减1,对应原数组区间段内的加减一操作。
选择任意一项 加1 或者 减1,对应原数组区间段内的加减一操作。
显然, 使数组全部变为0的最少操作方案就是 差分数组中负数和与正数和的绝对值的最大值
差分数组为0,意味着原数组每一项元素为相等。
a[i] = a[i - 1] + b[i],其中b[i]=0则a[i]=a[i - 1]
模拟案例:
案例A-B数组是0 3 1 1 3,把它变成0 0 0 0 0也是把对应的差分数组0 3 -2 0 2变成 0 0 0 0 0,那么对差分数组进行两次操作1(左加右减),差分数组变成0 3 0 0 0,A-B变成0 3 3 3 3(因为让第三个到第四个这个区间都加了1),差分数组进行3次操作2变成0 0 0 0 0(减1),A-B也会变成0 0 0 0 0(对第2个以后所有数减3次1)