题解:洛谷 P1614 爱与愁的心痛

【题目来源】

洛谷:P1614 爱与愁的心痛 - 洛谷

【题目描述】

最近有 \(n\) 个不爽的事,每句话都有一个正整数刺痛值(心理承受力极差)。爱与愁大神想知道连续 \(m\) 个刺痛值的和的最小值是多少,但是由于业务繁忙,爱与愁大神只好请你编个程序告诉他。

【输入】

第一行有两个用空格隔开的整数,分别代表 \(n\)\(m\)

\(2\) 到第 \((n+1)\) 行,每行一个整数,第 \((i+1)\) 行的整数 \(a_i\) 代表第 \(i\) 件事的刺痛值 \(a_i\)

【输出】

输出一行一个整数,表示连续 \(m\) 个刺痛值的和的最小值是多少。

【输入样例】

8 3
1
4
7
3
1
2
4
3

【输出样例】

6

【核心思想】

  1. 问题分析:给定一个长度为 \(n\) 的序列,需要找出所有长度为 \(m\) 的连续子序列中,和的最小值。本质是一个固定长度滑动窗口的最小和问题。由于 \(n\)\(m\) 通常不大(如 \(n \le 3000\)),可以直接枚举所有子序列并求和,也可以使用前缀和或滑动窗口优化。

  2. 算法选择

    • 双重循环枚举:对于每个起始位置 \(i\)\(0 \le i \le n-m\)),计算子序列 \(a[i..i+m-1]\) 的和,并更新最小值。时间复杂度 \(O(n \cdot m)\),在 \(n \le 3000\) 时完全可行。
    • 前缀和优化(可选):预处理前缀和数组 \(prefix\),则子序列和 \(sum(i, i+m-1) = prefix[i+m] - prefix[i]\),可将时间复杂度降为 \(O(n)\)。但本题数据规模下,双重循环已经足够。
  3. 关键步骤

    • 读入与存储:读取 \(n\)\(m\) 和序列 \(a[0..n-1]\)
    • 初始化最小值:将 min_sum 设置为一个很大的数(如 INT_MAX)。
    • 枚举所有长度为 \(m\) 的子序列
      • 外层循环 \(i\)\(0\)\(n-m\),表示子序列的起始位置。
      • 内层循环计算 \(a[i]\)\(a[i+m-1]\) 的和。
      • 将当前和与 min_sum 比较,取较小值。
    • 输出min_sum
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot m)\),对于 \(n \le 3000\) 可接受。
    • 空间复杂度:\(O(n)\),用于存储序列。
  5. 模拟与窗口求和

    • 固定窗口:本题中 \(m\) 是固定的,因此只需要考虑长度恰好为 \(m\) 的连续子序列。
    • 暴力枚举:对于入门题,直接枚举所有可能窗口并求和是最直观的思路,也容易理解和实现。
    • 边界处理:注意循环上限为 \(n-m\)(包含),确保窗口不越界。
    • 适用场景:这种固定长度窗口的最值问题,是滑动窗口算法的入门基础。当数据规模较小时,暴力枚举是可靠的解法。

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

int a[3000];             // 定义全局数组:存储输入的数字序列

int main()
{
    int n, m;            // 定义变量:n-数字总数,m-子序列长度
    int i, j;            // 定义循环变量
    int sum = 0;         // 定义变量:当前子序列的和
    int min = 2147483647;// 定义变量:最小子序列和,初始为最大整数值

    cin >> n >> m;       // 输入数字总数和子序列长度

    // 输入数字序列
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }

    // 特殊情况处理:当子序列长度等于总长度时
    if (m == n)
    {
        // 直接计算整个序列的和
        for (i = 0; i < n; i++)
        {
            sum += a[i];
        }
        min = sum;       // 整个序列的和就是最小值
    }
    else
    {
        // 常规情况:遍历所有可能的子序列
        for (i = 0; i <= n - m; i++)
        {
            sum = 0;     // 重置当前子序列和

            // 计算当前子序列的和
            for (j = i; j < i + m; j++)
            {
                sum += a[j];
            }

            // 更新最小子序列和
            if (sum < min)
                min = sum;
        }
    }

    cout << min;         // 输出最小子序列和

    return 0;            // 程序正常结束
}

【运行结果】

8 3
1
4
7
3
1
2
4
3
6
posted @ 2026-08-30 09:18  团爸讲算法  阅读(15)  评论(0)    收藏  举报