1 条题解

  • 0
    @ 2025-12-14 11:04:10

    一、题目回顾

    题目核心

    给定长度为L的线性位置序列,其中N个位置是香蕉(坐标严格递增),其余为苹果。可使用至多M次魔法将香蕉转为苹果,求转换后最长的连续苹果段长度

    输入输出要求

    • 输入:第一行N(香蕉数)、M(魔法次数)、L(总长度);第二行N个严格递增的整数,表示香蕉的位置。
    • 输出:最长连续苹果段的长度。

    二、用户代码的核心思路分析

    用户代码采用滑动窗口(双指针) 解决问题,核心逻辑围绕“维护一个最多包含M个香蕉的区间,该区间内的香蕉可全部转为苹果,区间整体即为连续苹果段”展开,具体思路如下:

    1. 状态标记

    用数组a标记每个位置的类型:

    • a[i] = 1:位置i是香蕉;
    • a[i] = 0:位置i是苹果。

    2. 滑动窗口定义

    • 左指针l、右指针r:表示当前窗口的左右边界,窗口[l, r]内的香蕉数量为sum
    • 核心约束:窗口内香蕉数量sum ≤ M(可通过魔法转为苹果,窗口整体为连续苹果段)。

    3. 窗口移动规则

    • 扩张右边界:若当前窗口内香蕉数sum ≤ M,说明窗口合法,先更新最长苹果段长度ans,再向右移动r扩大窗口;若新加入的r位置是香蕉,sum加1。
    • 收缩左边界:若当前窗口内香蕉数sum > M,说明窗口非法,需向右移动l收缩窗口;若移出的l位置是香蕉,sum减1。

    4. 终止条件

    当左指针l超过总长度L,或右指针r超过总长度L时,窗口无法再扩展/收缩,循环终止。

    三、用户代码(带详细逐行注释)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;  // 该常量未实际使用,仅占位
    // 如果某个区间的香蕉数量小于等于m  那就是合理的 可以选的
    int n,m,a[10000010],L,t,ans;  // 变量说明:
                                  // n:香蕉的数量;m:魔法道具使用次数;
                                  // a[]:标记位置是否为香蕉(a[i]=1是香蕉,0是苹果);
                                  // L:苹果和香蕉的总数量(总长度);
                                  // t:临时变量,用于读取香蕉位置;
                                  // ans:最终答案,最长连续苹果段长度
    
    // n是有n个香蕉   L是总距离
    // a[i]=0  认为坐标i上是 苹果
    // a[i]=1  认为坐标i上是 香蕉
    int main(){
        // 输入:香蕉数n、魔法次数m、总长度L
        cin>>n>>m>>L;
        // 遍历n个香蕉,标记对应位置为香蕉(a[t]=1)
        for(int i=1;i<=n;i++){
            cin>>t;  // 读取第i个香蕉的位置t
            a[t]=1;  // 标记位置t为香蕉
        }
        // 滑动窗口初始化:左指针l=1,右指针r=0(初始窗口为空),sum=0(窗口内香蕉数)
        int l=1,r=0,sum=0;
        // 滑动窗口主循环:左指针不超过总长度,且右指针不超过总长度时循环
        while(l<=L&&r<=L){
            // 情况1:当前窗口内香蕉数≤m(窗口合法,可扩展)
            if(sum<=m){
                ans=max(ans,r-l+1);  // 更新最长苹果段长度(当前窗口长度为r-l+1)
                r++;                 // 右指针右移,扩大窗口
                if(a[r]==1) sum++;   // 若新加入的位置是香蕉,窗口内香蕉数+1
            }else{
                // 情况2:当前窗口内香蕉数>m(窗口非法,需收缩)
                if(a[l]==1) sum--;   // 若左指针位置是香蕉,移出后窗口内香蕉数-1
                l++;                 // 左指针右移,收缩窗口
            }
        }
        // 输出最长连续苹果段长度
        cout<<ans;
        return 0;
    }
    

    四、样例走查(验证代码正确性)

    以样例输入为例,逐步模拟代码执行过程:

    样例输入

    5 2 100
    10 30 55 56 90
    

    初始化

    • n=5, m=2, L=100
    • a[10]=a[30]=a[55]=a[56]=a[90]=1,其余a[i]=0
    • l=1, r=0, sum=0, ans=0

    关键执行步骤(核心窗口阶段)

    1. 窗口扩张至[31, 89](对应香蕉55、56在窗口内):

      • r移动到55时,sum=1(≤2),继续扩张;
      • r移动到56时,sum=2(≤2),此时窗口包含55、56两个香蕉(刚好用完2次魔法);
      • 继续扩张r到89,窗口内香蕉数仍为2(≤2),窗口长度为89-31+1=59ans更新为59;
      • r移动到90时,sum=3(>2),触发左指针收缩:左指针从31右移,直到移出55(sum减为2),窗口重新合法。
    2. 最终ans保留最大值59,与样例输出一致。

    五、代码复杂度分析

    1. 时间复杂度

    • 滑动窗口的lr指针均最多遍历L次(L≤1e7),总循环次数为O(L)
    • 输入香蕉位置的循环为O(n)n≤1e5),可忽略。
    • 整体时间复杂度:O(L),在L≤1e7的范围内可通过所有测试用例。

    2. 空间复杂度

    • 数组a的大小为1e7+10,每个元素为int型(4字节),总占用约40MB,符合内存限制。
    • 其余变量为常数级,空间复杂度:O(L)

    六、代码补充说明

    1. 正确性保障

    • 窗口合法条件(sum≤m)确保区间内香蕉可全部转为苹果,窗口长度即为连续苹果段长度。
    • 每次扩张窗口前更新ans,确保不会遗漏当前窗口的最大长度。

    2. 边界情况处理

    • m≥n时:所有香蕉可转为苹果,窗口最终会覆盖[1, L]ans=L,正确。
    • n=0时:无香蕉,sum=0,窗口覆盖[1, L]ans=L,正确。
    • m=0时:窗口内不能有香蕉,会自动收缩为仅包含苹果的最长区间,正确。

    3. 可优化点(非必要)

    • 数组a可改用bool型(1字节),将空间占用降至10MB左右。
    • 无需存储所有位置,可通过香蕉位置的滑动窗口优化时间复杂度至O(n)(适合L极大的场景),但用户代码的逻辑已能正确解决问题。
    • 1

    信息

    ID
    5492
    时间
    1000ms
    内存
    256MiB
    难度
    6
    标签
    (无)
    递交数
    30
    已通过
    11
    上传者