【算法笔记】前缀和经典题目解析

前缀和可以用于快速求某一个区间各个值的和,原理是先预处理好所有的值,创建一个预处理好的数组,这个预处理的数组中的每个值都是原数组相同位置上前面的所有值相加。这是一种空间换取时间的做法。


1.一维前缀和

1.1【模板】前缀和

假设数组 f[N] 为我们的预处理数组,a[N] 为我们的原数组, 那创建预数组的方式为:

cpp 复制代码
f[i] = f[i - 1] + a[i];

有时候我们并不一定要保留原来值,只要能创建出预数组就可以;

想要还原出区别和也非常简单,假设 l 为左区间, r 为又区间,那这个区别的和为:

cpp 复制代码
f[r] - f[l - 1]

这就是前缀和的核心代码,这个算法主要是和别的算法一起搭在一起考察的:

cpp 复制代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int n, m;
LL f[N];

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		LL x; cin >> x;
		f[i] = f[i - 1] + x;
	}
	
	while (m--)
	{
		LL l, r; cin >> l >> r;
		cout << f[r] - f[l - 1] << endl;
	}
	return 0;
}

1.2最大子段和

这道题也可以使用前缀和来解决,因为我们要想找出最大的区间,我们是想要让减去的后面那段尽可能的小,所以我们可以定义一个变量premin使得减去前面的区间最小:

cpp 复制代码
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int f[N];
int n;

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x; cin >> x;
		f[i] = f[i - 1] + x;
	}
	int ret = -1e5;
	int premin = 0;
	for (int i = 1; i <= n; i++)
	{
		ret = max(ret, f[i] - premin);
		premin = min(premin, f[i]);
	}
	cout << ret << endl;
	return 0;
}

2.二维前缀和

2.1【模板】二维前缀和

二维前缀和与一维前缀和行驶的功能是完全一样的,只不过将一段扩展成了一个二维空间。

二维前缀和的预处理和还原方式与一维有些不同,但我们也是可以推出来的

我们设垂直方向为i,水平方向为j,可以得到:

更大的区别正是由这样一个个的子问题推导出来的,所以二维前缀和的公式可以为:

cpp 复制代码
f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + x

还原的推理公式也是同理我这里就不再赘述了

cpp 复制代码
#include <iostream>
using namespace std;
const int N = 1010;
typedef long long LL;
int n, m, q;
LL f[N][N];
LL a[N][N];

int main()
{
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			LL x; cin >> x;
			f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + x; 
		}
	}
	
	while (q--)
	{
		LL x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		LL ret = f[x2][y1 - 1] + f[x1 - 1][y2] - f[x1 - 1][y1 - 1];
		cout << f[x2][y2] - ret << endl;
	}
	
	return 0;
}

2.2激光炸弹

这道题是二维前缀和的经典运用,我们可以通过m计算出这个区间,注意边界问题即可:

cpp 复制代码
#include <iostream>
using namespace std;
const int N = 5010;
int a[N][N];
int f[N][N];
int n, m;

int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i++)
	{
		int x, y, v; cin >> x >> y >> v;
		x++; y++;
		a[x][y] += v;
	}
	for (int i = 1; i < N; i++)
	{
		for (int j = 1; j < N; j++)
		{
			f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + a[i][j]; 
		}
	}
	int ret = 0;
	m = min(m, N);
	for (int i = m; i < N; i++)
	{
		for (int j = m; j < N; j++)
		{
			int x2, y2; x2 = i; y2 = j;
			int x1, y1; x1 = i - m + 1; y1 = j - m + 1;
			int t = f[x2][y2] - f[x2][y1 - 1] - f[x1 - 1][y2] + f[x1 - 1][y1 - 1];
			ret = max(ret, t);
		}
	}
	cout << ret << endl;
	return 0;
}

相关推荐
多加点辣也没关系几秒前
数据结构与算法总章
数据结构·算法
影sir几秒前
STL容器——vector类
c++·算法·stl
Brilliantwxx2 分钟前
【C++】stack_queue与deque模版(模拟实现+认识+对比)
开发语言·c++·笔记·算法·list
爱吃香芋派OvO5 分钟前
ComfyUI 视频创作实战手册:节点搭建 + 性能优化 + 批量生成
人工智能·算法·机器学习
爱喝水的鱼丶5 分钟前
SAP-ABAP:ABAP Development Tools(ADT)安装配置学习分享教程(四篇连载)第四篇:ADT连接故障排查与环境迁移教程
运维·开发语言·数据库·学习·sap·abap
数智工坊7 分钟前
【深度学习RL】A3C:异步强化学习的革命——用CPU打败GPU的深度RL算法
论文阅读·人工智能·深度学习·算法·transformer
灵智实验室7 分钟前
PX4状态估计技术EKF2详解(三):EKF2 外部视觉融合——延迟后验状态与触发机制
算法·无人机·px 4
爱吃提升11 分钟前
Yifan Hu(适合大规模数据)大数据算法
开发语言·算法·php
Xpower 1711 分钟前
从PHM到AI Agent-如何用OpenClaw构建设备健康诊断智能体
网络·人工智能·学习·算法
一只旭宝11 分钟前
【C++入门精讲13】异常处理
c++