算法复盘——前缀和

一、思路

前缀和 :预处理数组,快速计算区间和,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;
 } 
相关推荐
yyyyy_abc6 小时前
ceph学习笔记
笔记·ceph·学习
晓梦林6 小时前
ximai靶场学习笔记
android·笔记·学习
Dlrb12116 小时前
C语言-指针三
c语言·算法·指针·const·命令行参数
nashane6 小时前
HarmonyOS 6学习:外接键盘CapsLock与长截图功能的实战调试与完整解决方案
学习·华为·计算机外设·harmonyos
IT19957 小时前
Dify笔记-创建自定义工具连接到自建的OpenAPI Schema服务
笔记
Tisfy7 小时前
LeetCode 2540.最小公共值:双指针(O(m+n))
算法·leetcode·题解·双指针
IronMurphy7 小时前
【算法四十七】152. 乘积最大子数组
算法
一口吃俩胖子7 小时前
【脉宽调制DCDC功率变换学习笔记021】时域性能准则
笔记·学习
starvapour8 小时前
SSH笔记
笔记·ssh
淘矿人8 小时前
Claude辅助DevOps实践
java·大数据·运维·人工智能·算法·bug·devops