领略算法真谛:前缀和

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!

我的博客:yuanManGan

我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记  闲言碎语小记坊 题山采玉

目录

前缀和

 ⼀维前缀和

题目理解:

思路讲解:

代码实现

⼆维前缀和

题目理解:

思路讲解:

代码实现:

题目试手

一维前缀和:

二维前缀和:


前缀和

前缀和与差分的核⼼思想是预处理,可以在暴⼒枚举的过程中,快速给出查询的结果,从⽽优化时间 复杂度。 是经典的⽤空间替换时间的做法。

解决某种算法呢一般要从题目入手,我们针对一类题目有一类的算法,不会凭空出现一类算法,而我们的前缀和算法就以这道题为插入吧:

 ⼀维前缀和

题⽬来源: ⽜客⽹
题⽬链接:【 模板】前缀和
难度系数: ★

题目理解:

题目很简单就给一个数组,再给一个区间,求区间类的数的和。

思路讲解:

暴力解法:

很多人上来的第一思路就和我一样(没学习前缀和之前),求一次区间就累加一次呗,但我们注意这里的数据范围,如果这样实现的话,时间复杂度就成了n*q(1e5*1e5)显然会超时。这时我们就提出我们本文要学习的前缀和算法了

前缀和:

我们可以对这个数组进行预处理,算出每个下标的前面的数累加的结果,比如:

就是创建一个数组来有预处理原数组,从而创建一个前缀和数组,当要求某一段区间时,比如求2~4区间的和时我们仅需要,使用前缀和数组的4下标减去1下标的数,求x~y区间,也就是求前缀和数组中的y下标减去x-1下标即可

代码实现

由于第一种做法太简单大家自己实现即可

前缀和:

#include<iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;

int n, q;
//不需要创建原数组
//LL a[N];
//前缀和数组
LL f[N];

int main()
{
    cin >> n >> q;
	//预处理
	for (int i = 1; i <= n; i++)//下标从1开始避免边界情况
	{
		int x; cin >> x;
		f[i] = f[i - 1] + x;
	}
	//q次询问
	while (q--)
	{
		int x, y; cin >> x >> y;
		cout << f[y] - f[x - 1] << endl;
	}

    return 0;
}

有了一维前缀和的铺垫,我们就可以讲解二维前缀和问题了:

⼆维前缀和

二维前缀和,和一维前缀和思路是一样的但实现方法不同,二维前缀和需要使用二维数组来进行预处理。

题⽬来源: ⽜客⽹
题⽬链接: 【模板】⼆维前缀和
难度系数: ★

题目理解:

给个二维数组,求两个坐标(一个为左上角,一个为右下角)组成的长方形的所有点的和。

思路讲解:

暴力解法:

两次for循环,直接加,时间复杂度依旧为nq(1e4*1e5)会出现超时的。

二维前缀和:
我们定义一个二维数组f [ ] [ ]来预处理a[ ] [ ]数组中的元素,

f[x] [y] = f[x - 1] [y - 1] + f[x] [y - 1] + f[x - 1] [y] - a[x] [y]; 

这样就可以对原数组进行预处理。

那我们怎么得到所求的答案呢?看图

A = (A + B + C + D) - (B + C) - (C - D) + C

答案 == f (x2) (y2) - f (x2)(y1 - 1) - f(x1 - 1)(y2) + f(x1- 1)( y1 - 1);

这里要注意我们是以x1y1为左上角,所以这里涉及到x1y1的地方都进行的减一操作。

代码实现:

#include<iostream>
using namespace std;
typedef long long LL;
const int N = 1e3 + 10;
//前缀和数组
LL f[N][N];
int n, m, q;
int main()
{
	cin >> n >> m >> q;
	//预处理操作
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			int x; cin >> x;
			f[i][j] = x + f[i][j - 1] + f[i - 1][j] - f[i - 1][j - 1];
		}
	}
	//q次操作
	while (q--)
	{
		int x1, y1, x2, y2; 
		cin >> x1 >> y1 >> x2 >> y2;
		cout << f[x2][y2] - f[x2][y1 - 1] - f[x1 - 1][y2] + f[x1 - 1][y1 - 1] << endl;
	}
}

上述分享的仅为一般模板,题目还是要根据不同题目不同程度地修改模板。

题目试手

一维前缀和:

最大字段和(洛谷)

二维前缀和:

激光炸弹(洛谷)

我的答案贴在最后了

最大字段和:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
//前缀和数组
LL 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;
	}
	LL ret = -1e20;
	LL prevmin = 0;
	for (int i = 1; i <= n; i++)
	{
		ret = max(ret, f[i] - prevmin);
		prevmin = min(prevmin, f[i]);
	}
	return 0;
}

激光炸弹:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
const int N = 5e3 + 10;
int f[N][N];
int a[N][N];
int n, m;
int main()
{
	cin >> n >> m;
	while (n--)
	{
		int x, y, v; cin >> x >> y >> v;
		a[++x][++y] += v;
	}
	n = 5009;
	for (int i = 1; i <= n;i ++)
		for (int j = 1; j <= n; j++)
			f[i][j] = a[i][j] + f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1];
	int ret = 0;
	m = min(m, n);
	for (int x2 = m; x2 <= n; x2++)
	{
		for (int y2 = m; y2 <= n; y2++)
		{
			int x1 = x2 - m + 1, y1 = y2 - m + 1;
			int sum = f[x2][y2] - f[x1 - 1][y2] - f[x2][y1 - 1] + f[x1 - 1][y1 - 1];
			ret = max(sum, ret);
		}
	}
	cout << ret << endl;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值