算法复盘——前缀和

一、思路

前缀和 :预处理数组,快速计算区间和,O(n)预处理→O(1)查询。

二、例题

一维前缀和

1.区域和检索 - 数组不可变

303. 区域和检索 - 数组不可变 - 力扣(LeetCode)

代码实现:
cpp 复制代码
class NumArray {
    private:
        vector <int> pre_sum;
    public:
        NumArray(vector<int>& nums) {
            int n = nums.size();
            pre_sum.resize(n+1, 0);
            for(int i = 0; i < n; i++)  pre_sum[i+1] = pre_sum[i] + nums[i];
        }
        
        int sumRange(int left, int right) {
            return pre_sum[right + 1] -pre_sum[left];
        }
};

2.最大子段和

P1115 最大子段和 - 洛谷

代码实现:
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

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

3.和为 K 的子数组

560. 和为 K 的子数组 - 力扣(LeetCode)

思路:

先计算前缀和数组,然后在遍历过程中维护前面出现过的最小前缀和。对于每个位置,用当前前缀和减去最小前缀和,就能得到以当前位置结尾的最大子数组和。

代码实现:
cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        mp[0] = 1; // 前缀和为0的情况出现1次(对应pre_sum[0])
        int pre_sum = 0, cnt = 0;
        for(int num : nums) {
            pre_sum += num;
            // 查找之前有多少个前缀和等于 pre_sum - k
            if(mp.contains(pre_sum - k) )  cnt += mp[pre_sum - k];
            mp[pre_sum]++;
        }
        return cnt;
    }
};

二位前缀和

1.激光炸弹

P2280 [HNOI2003] 激光炸弹 - 洛谷

问题分析:
  • 地图坐标范围:0 ≤ x,y ≤ 5000
  • 同一位置可能有多个目标,价值要累加
  • 炸弹是 R×R 正方形 ,目标必须在正方形内部才被摧毁
  • 目标:找一个 R×R 正方形,使内部总价值最大
思路:

先把所有点的价值累加进矩阵,再算前缀和。然后枚举每个 R×R 正方形的右下角,用前缀和公式 O (1) 算和,最后取最大。

代码实现:
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 5010; 
int n, m;
LL a[N][N], st[N][N];
int main()
{
	cin >> n >> m;
	while(n--)
	{
		int x, y, v; cin >> x >> y >> v;
		a[++x][++y] += v;
	}
	n = 5001;
	// 前缀和数组
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			st[i][j] = st[i-1][j] + st[i][j-1] - st[i-1][j-1] + a[i][j];
		}
	 } 
	 // 枚举炸弹的位置
	 LL 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;
	 		LL tmp = st[x2][y2] - st[x1-1][y2] - st[x2][y1-1] + st[x1-1][y1-1]; 
	 		ret = max(ret, tmp);
		 }
	  } 
	  cout << ret << endl;
	return 0;
 } 
相关推荐
ulias2122 小时前
智能指针简述
开发语言·c++·算法
寻寻觅觅☆2 小时前
东华OJ-基础题-58-素数表(C++)
开发语言·c++·算法
AI成长日志2 小时前
【强化学习专栏】深度拆解:多智能体强化学习核心理论与工程实践
算法
Flying pigs~~2 小时前
基于TF_IDF和Bagging的文本分类全过程
算法·随机森林·机器学习·nlp·文本分类
树獭叔叔2 小时前
FFN 激活函数深度解析:从 ReLU 到 SwiGLU 的演进之路
算法·aigc·openai
啊我不会诶3 小时前
最小生成树
c++·笔记·学习·算法
liuyao_xianhui3 小时前
优选算法_栈_删除字符中的所有相邻重复项_C++
开发语言·数据结构·c++·python·算法·leetcode·链表
STLearner3 小时前
AI论文速读 | 元认知监控赋能深度搜索:认知神经科学启发的分层优化框架
大数据·论文阅读·人工智能·python·深度学习·学习·机器学习
老虎06273 小时前
LeetCode热题100 刷题笔记(第三天)链表 「两数相加」
笔记·leetcode·链表