【基础算法】算法的“预谋”:前缀和如何改变游戏规则

【基础算法】算法的“预谋”:前缀和如何改变游戏规则
在这里插入图片描述
🔭 个人主页:散峰而望

《C语言:从基础到进阶》《编程工具的下载和使用》《C语言刷题》《算法竞赛从入门到获奖》《人工智能》《AI Agent》

愿为出海月,不做归山云


🎬博主简介

在这里插入图片描述
请添加图片描述

【基础算法】算法的“预谋”:前缀和如何改变游戏规则


前言

在算法设计与优化中,前缀和是一种简单却强大的技巧,能够将复杂问题转化为高效计算。无论是处理一维数组的区间求和,还是解决二维矩阵的子矩阵问题,前缀和都能通过预处理将时间复杂度从线性降低到常数级别,彻底改变问题的解决方式。

从经典的最大子段和问题到实际应用中的“激光炸弹”场景,前缀和不仅展现了数学的优雅,更体现了算法思维的灵活性。本文将深入探讨一维和二维前缀和的原理与应用,揭示其如何通过“预谋”式的预处理,在竞赛与工程中成为改变游戏规则的关键。

前缀和

前缀和与差分的核心思想是预处理,可以在暴力枚举的过程中,快速给出查询的结果,从而优化时间复杂度。

是经典的用空间替换时间的做法。

前缀和: 快速求出数组中某一段区间和,时间复杂度为 O(1)

1.1 一维前缀和

1.1.1 前缀和

前缀和

在这里插入图片描述

算法原理:

  1. 暴力模拟实现,从指定的下标进行循环,时间复杂度为 O(n * q) 会超时
  2. 前缀和:
    a. 先预处理出来一个前缀和数组
    f[i] 表示:区间 [1, i] 中所有元素和
    f[i] = f[i - 1] + a[i]
    b. 查询 [l, r] 区间和,则 f[r] - f[l - 1]
在这里插入图片描述
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;constint N =1e5+10;typedeflonglong LL;int n, q; LL a[N]; LL f[N];//前缀和数组intmain(){ cin >> n >> q;for(int i =1; i <= n; i++) cin >> a[i];//处理前缀和for(int i =1; i <= n; i++){ f[i]= f[i -1]+ a[i];}//处理q次询问while(q--){int l, r; cin >> l >> r; cout << f[r]- f[l -1]<< endl;}return0;}

1.1.2 最大子段和

最大子段和

在这里插入图片描述

算法竞赛:

关于这道题的解法有很多种,我们先介绍利用「前缀和」的解法。

考虑以 i 位置的元素 a[i]「为结尾」的最大子段和:

  • 在求「区间和」时,相当于是用 f[i] 减去 i 位置前面的某一个 f[x]
  • 如果想要「最大子段和」,也就是「最大区间和」,那么用 f[i] 减去一个「前驱最小值」即可

参考代码:

#include<iostream>usingnamespace std;typedeflonglong LL;constint N =2e5+10;int n; LL f[N];//前缀和数组intmain(){ cin >> n;for(int i =1; i <= n; i++){ LL 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]);} cout << ret << endl;}

1.2 二维前缀和

1.2.1 二维前缀和

二维前缀和

在这里插入图片描述

根据题目要求,我们知道题目的求和情况如下:

在这里插入图片描述

算法原理:

  1. 暴力模拟
    查询哪个子区间,用两层 for 循环,累加。时间复杂度高 O(q * n * m)
  2. 二维前缀和
    快速查询二维数组中的某个子矩阵所有元素和,时间复杂度 O(n * m) + O(q)
    a.预处理出二维前缀和矩阵 f[i][j] 表示从 [1, 1] 到 [i, j] 区域内所有的元素和。
    b.用二维前缀和的矩阵解决问题。
  • 创建前缀和矩阵:f[i][j]=f[i−1][j]+f[i][j−1]−f[i−1][j−1]+a[i][j]
在这里插入图片描述
  • 查询以 (x1, y1) 为左上角 (x2, y2) 为右下角的子矩阵的和
在这里插入图片描述
#include<iostream>usingnamespace std;typedeflonglong LL;constint N =1010;int n, m, q; LL f[N][N];intmain(){ cin >> n >> m >> q;//预处理前缀和矩阵 for(int i =1; i <= n; i++){for(int j =1; j <= m; j++){ LL x; cin >> x; f[i][j]= f[i -1][j]+ f[i][j -1]- f[i -1][j -1]+ x;}}//处理q查询while(q--){int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; cout << f[x2][y2]- f[x1 -1][y2]- f[x2][y1 -1]+ f[x1 -1][y1 -1]<< endl;}return0;}

1.2.2 激光炸弹

激光炸弹

在这里插入图片描述

因为题目要求若目标位于爆破正方形的边上,该目标不会被摧毁,则以下这些情况炸弹威力会减弱

在这里插入图片描述

即如果想尽可能炸更多目标,炸弹边和矩阵边重叠。

算法原理:

暴力枚举坐标系中所有边长为 R 的正方形,找出正方形内目标价值最大的目标即可。

枚举右下角 [x2, y2] 从而得到整个正方形,用一个二维矩阵将所有目标的价值存起来,其中 a[i][j] 就表示 [i, j] 位置的目标价值之和。

#include<iostream>usingnamespace std;constint N =5010;int n, m;int a[N][N];int f[N][N];//前缀和intmain(){ cin >> n >> m;while(n--){int x, y, v; cin >> x >> y >> v; x++, y++;//下标从1开始 a[x][y]+= v;//同一个位置可能有多个目标 }//预处理 n =5001;for(int i =1; i <= n; i++){for(int j =1; j <= n; j++){ f[i][j]= f[i -1][j]+ f[i][j -1]- f[i -1][j -1]+ a[i][j];}}int ret =0; m =min(m, n);// 如果 m 很大,相当于就是把整个区域全部摧毁 // 枚举所有边?为 m 的正方形 for(int x2 = m; x2 <= n; x2++){for(int y2 = m; y2 <= n; y2++){int x1 = x2 - m +1, y1 = y2 - m +1; ret =max(ret, f[x2][y2]- f[x1 -1][y2]- f[x2][y1 -1]+ f[x1 -1][y1 -1]);}} cout << ret << endl;return0;}

结语

前缀和作为一种基础而强大的算法技巧,通过预处理数据将复杂度从线性降至常数级别,彻底改变了处理区间问题的游戏规则。从一维数组的快速求和、最大子段和优化,到二维矩阵的区域统计、激光炸弹效果模拟,前缀和以空间换时间的策略展现了极高的效率。

掌握前缀和不仅能够解决经典问题,更能为复杂场景(如动态规划、数据结构优化)提供关键思路。其思想可以延伸到更高维度的数据处理中,成为算法设计中不可或缺的“预谋”手段。理解并灵活运用前缀和,是提升算法问题解决能力的重要一步。

愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天



Read more

❿⁄₁₄ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击实践 ➱ 传递Net-NTLMv2哈希

❿⁄₁₄ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击实践 ➱ 传递Net-NTLMv2哈希

郑重声明:本文所涉安全技术仅限用于合法研究与学习目的,严禁任何形式的非法利用。因不当使用所导致的一切法律与经济责任,本人概不负责。任何形式的转载均须明确标注原文出处,且不得用于商业目的。 🔋 点赞 | 能量注入 ❤️ 关注 | 信号锁定 🔔 收藏 | 数据归档 ⭐️ 评论 | 保持连接💬 🌌 立即前往 👉晖度丨安全视界🚀 ▶ 信息收集  ▶ 漏洞检测 ▶ 初始立足点  ▶ 权限提升 ▶ 横向移动 ➢ 密码攻击 ➢ 传递Net-NTLMv2哈希🔥🔥🔥 ▶ 报告/分析 ▶ 教训/修复 目录 1.密码破解 1.1 破解Windows哈希实践 1.1.4 传递Net-NTLMv2哈希概述 1.1.4.1 攻击背景 1.1.4.2 攻击流程 1.1.

By Ne0inhk
C语言指针与数组的深度关联及实战应用

C语言指针与数组的深度关联及实战应用

C语言指针与数组的深度关联及实战应用 💡 学习目标:掌握指针与数组的内在联系,熟练运用指针操作数组元素,解决实际开发中的数组遍历、数据交换等问题;学习重点:数组名的本质、指针算术运算操作数组、指针数组与数组指针的区别及应用。 38.1 数组名与指针的关系 在C语言中,数组和指针有着密不可分的联系。很多初学者会混淆数组名和指针变量的概念,其实二者既有关联,又有本质区别。 38.1.1 数组名的本质 💡 数组名在大多数情况下会被编译器隐式转换为指向数组首元素的常量指针。 我们来看一段简单的代码: #include<stdio.h>intmain(){int arr[5]={10,20,30,40,50};printf("数组首元素地址:%p\n", arr);printf("数组首元素地址:%p\n&

By Ne0inhk
【LeetCode经典题解】:从前序和中序遍历构建二叉树详解

【LeetCode经典题解】:从前序和中序遍历构建二叉树详解

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:Java.数据结构 【前言】 二叉树构造是算法中递归分治思想的经典应用,而通过前序与中序遍历序列还原二叉树,更是力扣考察二叉树特性的高频题。前序“根左右”、中序“左根右”的遍历特性,是逐层确定根节点、划分左右子树的关键。本文将从递归分治思想出发,拆解该问题的实现逻辑,分析代码设计的核心细节。 文章目录: * 一、从前序遍历和中序遍历构造二叉树 * 二、思路分析 * 三、代码详解 * 1.代码分析 * 2.代码展示 一、从前序遍历和中序遍历构造二叉树 链接直达:从前序遍历和中序遍历构造二叉树 二、思路分析 根据递归分治思想: 前序遍历:根节点—>左子树—>右子树;找到前序序列的第一个元素就是根节点;中序遍历:

By Ne0inhk
链表经典OJ问题详解

链表经典OJ问题详解

文章目录 前言 1. 删除链表中等于给定值 val 的所有结点 2. 反转一个单链表 3. 链表的中间结点 4. 链表中倒数第k个结点 5. 合并两个有序链表 6. 链表分割 7. 链表的回文结构 8. 相交链表 9. 判断链表中是否有环 10. 返回链表开始入环的第一个结点 结语 前言 链表是一种基础且重要的数据结构,在程序设计中有着广泛的应用。由于其物理存储的非连续性,链表在插入、删除操作上具有独特的优势,但也给某些操作(如随机访问)带来了挑战。在技术面试和算法竞赛中,链表相关的题目出现频率极高,熟练掌握链表的常见操作和经典问题的解法,是每个程序员必备的技能。本文精选了10道经典的链表OJ题目,从思路分析到C语言代码实现,逐步详解,并穿插了快慢指针、哑结点等常用技巧的讲解,每道题目都附带了对应的在线练习链接,方便读者动手实践。希望能帮助读者深入理解链表,轻松应对各类链表问题。 1. 删除链表中等于给定值 val

By Ne0inhk