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

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


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

相关推荐
YH55269842 小时前
GPT‑5.6 Sol 原本支持 1M 上下文,Codex 现已放开此前限制,如何看待这次调整?
java·jvm·人工智能·gpt·算法·chatgpt
chushiyunen2 小时前
欧洲地理笔记
笔记
吃着火锅x唱着歌2 小时前
Effective C++ 学习笔记 条款43 学习处理模板化基类内的名称
c++·笔记·学习
felixking3 小时前
C++20 协程
开发语言·c++·协程
weixin_431600444 小时前
Agent Workflow 学习向:Code 节点,比模板更强的变量加工
后端·python·学习·ai·
遥感知识服务4 小时前
从局部阈值、双极化到暗地表剔除:NASA OPERA DSWx-S1全球动态水体算法拆解
大数据·人工智能·深度学习·神经网络·算法·机器学习
晚风醉蝶4 小时前
1-13-TimSort
算法
ZJU_统一阿萨姆4 小时前
【算子开发】算子融合与Softmax_LayerNorm实现
人工智能·算法·语言模型·硬件架构
Xuanzang Road4 小时前
第 40 届学习强企玄奘路戈壁挑战赛:在绝境中重塑自我
学习
tju新生代魔迷5 小时前
Python学习日记3
学习