【洛谷刷题 | 第四天】

本系列文章我将总结我在刷算法题所用到的知识,如果你也在刷算法并且是新手,我相信这系列文章会很适合你。

【洛谷刷题 | 第四天】

今日题目:

1.最大子段和(前缀和,贪心)

链接:P1115 最大子段和

给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。

案例:

cpp 复制代码
输入:                                  输出
7                                       4
2 -4 3 -1 2 -4 3

通过前缀和将 "连续子数组和" 转化为差值问题后,核心是贪心策略:对每个位置,尽可能用最小的前缀和去减当前前缀和,以得到最大的差值(即最大子数组和),避免了暴力枚举所有子数组的高复杂度。

题解:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[200009] = {0},b[2000009] = {0},c[2000009] = {0};
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    b[0] = a[0];
    for(int i=1;i<n;i++){
        b[i] = a[i] + b[i-1];
    }
    int minn = min(0,b[0]);c[0] = b[0];
    for(int i=1;i<n;i++){
        c[i] = b[i] - minn;
        minn = min(b[i],minn);
    }
    int maxx = -1e5;
    for(int i=0;i<n;i++){
         maxx = max(c[i],maxx);
    }
    cout<<maxx;
}
知识点:前缀和,贪心

前缀和就是从数组开头到当前位置的所有元素的累加和。

cpp 复制代码
假设有数组 a = [a₀, a₁, a₂, ..., aₙ₋₁]
定义前缀和数组 b,其中:
b[0] = a[0](第一个元素的前缀和就是它自己)
b[1] = a[0] + a[1](前 2 个元素的和)
b[2] = a[0] + a[1] + a[2](前 3 个元素的和)
...
b[i] = a[0] + a[1] + ... + a[i](前 i+1 个元素的和)

前缀和的核心价值:快速计算任意连续子数组的和。

cpp 复制代码
比如想算数组中从第 j 个元素到第 i 个元素的和(j ≤ i):
不用重新遍历 j 到 i 累加;
直接用公式:a[j] + a[j+1] + ... + a[i] = b[i] - b[j-1];
特殊情况:如果 j=0(从第一个元素开始),则和为 b[i](因为 b[-1] 视为 0)。

贪心就是:

每一步都做出当前看起来最优的选择,不考虑全局后果,最终希望通过每一步的局部最优,得到全局最优的结果。

2.领地选择(前缀和)

链接:P2004 领地选择

给定一个 N行M列 的二维地图(每个位置有对应的土地价值,可正可负),需要在地图上找到一个 C×C 的正方形区域,使得该区域内所有地块的价值总和最大。最终输出这个正方形区域左上角的坐标 (X,Y)(题目保证最优解唯一)。

案例:

cpp 复制代码
输入                                       输出
3 4 2                                      1 2
1 2 3 1
-1 9 0 2
2 0 1 1

这道题是一维前缀和的提升版 - 二维前缀和。

先计算每行的一维前缀和,再基于行前缀和累加得到二维前缀和,d [i][j] 表示从地图左上角 (1,1) 到 (i,j) 的矩形区域总价值;再遍历所有能放下 C×C 正方形的左上角坐标 (i,j),通过二维前缀和公式计算该正方形的总价值;最后对比所有合法正方形的总价值,记录最大值对应的左上角坐标,最终输出该坐标。

题解:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[1001][1001],b[1001][1001],d[1001][1001];
int main()
{
    int n,m,c;
    cin>>n>>m>>c;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            b[i][j] = a[i][j] + b[i][j-1];
            d[i][j] = b[i][j] + d[i-1][j];
        }
    }
        int maxx = INT_MIN;;int x =1,y=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if((i+c-1)>n||(j+c-1)>m) continue;
                 int q = d[i + c-1][j + c-1] + d[i - 1][j - 1] - d[i - 1][j + c-1] - d[i + c-1][j - 1];
               if(maxx<q){
                   maxx=q;
                   x=i,y=j;
               }
            }
        }
    cout<<x<<" "<<y;
    
}

3.语文成绩(差分)

链接:P2367 语文成绩

给定 n 个学生的初始成绩,执行 p 次加分操作(每次给第 x 到第 y 个学生每人加 z 分),最终输出全班的最低成绩。

案例:

cpp 复制代码
输入                                输出
3 2                                 2
1 1 1
1 2 1
2 3 1

这道题我们可以利用差分数组 d 替代直接遍历区间加分(降低复杂度),对每次 x 到 y 加 z 的操作,仅需在 d[x] 加 z、d[y+1] 减 z;之后对差分数组 d 做前缀和计算,得到每个学生实际需要增加的分数;最后将每个学生的初始成绩 a[i] 加上对应加分 d[i],遍历过程中实时记录所有成绩的最小值,最终输出该最小值。

题解:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[5000009];
int d[5000009];
int minn = INT_MAX;
int main()
{
    int n,p;
    cin>>n>>p;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int x,y,z;
    for(int i=0;i<p;i++)
    {
        cin>>x>>y>>z;
        d[x]+=z;
        d[y+1]-=z;
    }
  
    for(int i=1;i<=n;i++)
    {
        d[i]+=d[i-1];
    }
    for(int i=1;i<=n;i++)
    {
        a[i]+=d[i];
        if(minn>a[i])
		{
			minn=a[i];
		}
    }
   cout<<minn;
}
知识点:差分

差分是一种预处理数组的技巧,核心是用一个 "差分数组" 记录原数组中相邻元素的差值,目的是把 "区间批量修改" 转化为 "单点修改",降低操作复杂度。

例如:

cpp 复制代码
原数组 a = [a₁, a₂, a₃, ..., aₙ]
定义差分数组 d:
d₁ = a₁(第一个元素和原数组一致)
dᵢ = aᵢ - aᵢ₋₁(i≥2,即当前元素减前一个元素的差值)

差分的核心价值:快速处理数组的区间加减操作。

比如想给原数组中从 x 到 y 的所有元素都加 z:

不用遍历 x 到 y 逐个加; 只需对差分数组做 2 次单点修改:d[x] += z、d[y+1] -= z;

最后对差分数组做前缀和,就能还原出修改后的原数组。

举一个例子:

cpp 复制代码
假设原成绩a = [10, 20, 30, 40],现在要给第 2 到 3 个学生各加 5 分:
差分数组初始d = [0, 0, 0, 0, 0](多开一位避免越界);
区间加分:d[2] +=5  d[4] -=5 
对 d 做前缀和:
d[1] = 0
d[2] = 0+5=5
d[3] = 5+0=5
d[4] = 5+(-5)=0
最终成绩:10+0=10、20+5=25、30+5=35、40+0=40,

最后:

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
bai_lan_ya2 小时前
使用linux的io文件操作综合实验_处理表格
linux·服务器·算法
计算机安禾2 小时前
【C语言程序设计】第36篇:二进制文件的读写
c语言·开发语言·c++·算法·github·visual studio code·visual studio
ZPC82102 小时前
OLOv11 + 深度相机的方案实现高精度3D定位
人工智能·数码相机·算法·机器人
_日拱一卒2 小时前
LeetCode:字母异位词分组
算法·leetcode·职场和发展
Dfreedom.2 小时前
机器学习经典算法全景解析与演进脉络(监督学习篇)
人工智能·学习·算法·机器学习·监督学习
2301_807367192 小时前
C++代码风格检查工具
开发语言·c++·算法
Morwit2 小时前
*【力扣hot100】 215. 数组中的第K个最大元素
数据结构·c++·算法·leetcode·职场和发展
奔袭的算法工程师2 小时前
用AI写天线阵列排布算法
人工智能·算法·信号处理
ab1515172 小时前
3.20二刷基础121、127,完成进阶61、62
数据结构·算法·排序算法