题解:洛谷 P3009 Profits

【题目来源】

洛谷:P3009 [USACO11JAN] Profits S - 洛谷

【题目描述】

奶牛们开始了新的生意,它们的主人约翰想知道它们到底能做得多好。这笔生意已经做了 \(N(1\le N\le 10^5)\) 天,每天奶牛们都会记录下这一天的利润 \(P_i(\vert Pi\vert ≤1,000)\)

约翰想要找到奶牛们在连续的时间期间所获得的最大的总利润。(注:连续时间的周期长度范围从第一天到第 \(N\) 天)。

请你写一个计算最大利润的程序来帮助他。

【输入】

第一行,一个单独的整数 \(N\)

接下来 \(N\) 行,第 \(i+1\) 行包括一个单独的整数 \(P_i\)

【输出】

一行,一个整数,表示任意连续时间段利润最大和的数值。

【输入样例】

7
-3
4
9
-2
-5
8
-3

【输出样例】

14

【核心思想】

  1. 问题分析:给定 \(N\) 天的利润序列 \(P_i\)(可为负数),求连续子数组的最大和。这是一个经典的最大子段和/线性DP问题,关键在于用动态规划维护以第 \(i\) 天结尾的连续子数组的最大和。

  2. 算法选择

    • 线性DP(Kadane算法)\(a[i]\) 表示以第 \(i\) 天结尾的连续子数组的最大和,转移方程为 \(a[i] = \max(a[i-1] + P_i,\ P_i)\),即要么延续前面的子数组,要么从第 \(i\) 天重新开始
  3. 关键步骤

    • 读取输入\(N\)\(N\) 天的利润 \(P_i\)
    • 初始化\(maxn = -\infty\)
    • 线性DP\(i\)\(1\)\(N\)):
      • \(a[i] = \max(a[i-1] + a[i],\ a[i])\):以第 \(i\) 天结尾的最大子段和
      • \(maxn = \max(maxn,\ a[i])\):更新全局最大值
    • 输出 \(maxn\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\)
    • 空间复杂度:\(O(1)\),可滚动优化为仅维护前一个状态
  5. Kadane算法的核心思想

    • 局部最优子结构:以第 \(i\) 天结尾的最大子段和,要么包含第 \(i-1\) 天的最优子段(延续),要么只包含第 \(i\) 天(重新开始)
    • 负数处理:当 \(a[i-1] < 0\) 时,\(a[i-1] + P_i < P_i\),自动选择从 \(i\) 重新开始,避免负数拖累
    • 全局最大值追踪:由于最大子段和可能出现在任意位置,需要维护全局最大值
    • 适用于"连续子数组最大和"类经典问题,核心是 \(O(N)\) 的线性递推

【解题思路】

【算法标签】

普及- #线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, a[100005], maxn=-1e9;
int main()
{
    cin >> n;  // 输入n
    for (int i=1; i<=n; i++) {  // 输入n个数
        cin >> a[i];
    }
    for (int i=1; i<=n; i++) {  // 使用连续子序列最大和的模板
        a[i] = max(a[i]+a[i-1], a[i]);
        maxn = max(maxn, a[i]);  // 每次计算完需要判断最大值
    }
    cout << maxn << endl;  // 输出最大值
    return 0;
}

【运行结果】

7
-3
4
9
-2
-5
8
-3
14
posted @ 2026-08-28 15:57  团爸讲算法  阅读(4)  评论(0)    收藏  举报