算法复盘——前缀和

一、思路

前缀和 :预处理数组,快速计算区间和,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 小时前
基于CPS-SPWM链式STATCOM系统在电压不平衡环境下控制策略的simulink建模与仿真
算法
Jerry2 小时前
LeetCode 202. 快乐数
算法
hans汉斯3 小时前
基于改进交叉熵损失函数与Transformer的心电信号高风险分类研究
功能测试·深度学习·算法·yolo·目标检测·分类·transformer
Jerry3 小时前
LeetCode 349. 两个数组的交集
算法
YuK.W4 小时前
Leetcode100: 70.爬楼梯、118.杨辉三角、198.打家劫舍
java·算法·leetcode
随意起个昵称4 小时前
状压dp-基础题目2([USACO12MAR] Cows in a Skyscraper G)
c++·算法·动态规划
Funing75 小时前
FreeRTOS学习day1:Keil 工程配置与 FreeRTOS 链表机制理解
数据结构·学习·链表
心中有国也有家5 小时前
使用 DevEco Studio 配置 Flutter 鸿蒙签名
学习·flutter·华为·harmonyos
灯澜忆梦5 小时前
MySQL 完整学习路线
学习·mysql
Jerry5 小时前
LeetCode 1002. 查找共用字符
算法