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

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


1.一维前缀和

1.1【模板】前缀和

假设数组 fN 为我们的预处理数组,aN 为我们的原数组, 那创建预数组的方式为:

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;
}

相关推荐
LinXunFeng2 小时前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
JieE2121 天前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
JieE2122 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack202 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树2 天前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2123 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2123 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术3 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦3 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试