算法复盘——前缀和

一、思路

前缀和 :预处理数组,快速计算区间和,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;
 } 
相关推荐
计算机安禾2 小时前
【数据库系统原理】第16篇:范式理论(下):多值依赖与第四范式——消除非平凡的非函数依赖
算法
sensen_kiss2 小时前
CPT304 SoftwareEngineeringII 软件工程 2 Pt.5 软件复用(Software Reuse)
学习·软件工程
lqqjuly2 小时前
一致性模型深度解析
人工智能·深度学习·算法
RisunJan2 小时前
Linux命令-patch (为开放源代码软件安装补丁程序)
linux·服务器·算法
xian_wwq2 小时前
【学习笔记】倾斜摄影、高斯泼溅(3DGS)、点云与数字孪生“族谱”全盘点
笔记·学习·3d
伶俜662 小时前
# ✨ 零基础学 ArkUI 动画(专题一):从 animateTo 到 Lottie,一篇吃透全部
学习·华为·harmonyos
伶俜662 小时前
# [特殊字符] 零基础学 ArkUI 数据持久化(专题三):5 种存储方案深度对比
学习·华为·wpf·harmonyos
一条大祥脚2 小时前
ABC460贪心|多源BFS|数论|计数|线段树|树的直径
算法·宽度优先
小欣加油2 小时前
leetcode121买卖股票的最佳时机
数据结构·c++·算法·leetcode·职场和发展
Lucky_ldy2 小时前
51单片机的学习终(结合中科协的个人自用笔记)
笔记·学习·51单片机