2.1 P1115 最大子段和
题目描述
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n。
第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai。
输出格式
输出一行一个整数表示答案。
输入输出样例 #1
输入 #1
输出 #1
说明/提示
######## 样例 1 解释
选取 [3,5] 子段 {3,−1,2},其和为 4。
######## 数据规模与约定
- 对于 40% 的数据,保证 n≤2×103。
- 对于 100% 的数据,保证 1≤n≤2×105,−104≤ai≤104。
初始思路
最直接的想法是,枚举所有可能的连续子数组,并计算它们的和,然后找出最大的那个。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int a[n];
for(int i=0; i<n; i++) cin >> a[i];
int max_sum = INT_MIN;
for(int i=0; i<n; i++) {
int current_sum = 0;
for(int j=i; j<n; j++) {
current_sum += a[j];
if(current_sum > max_sum) {
max_sum = current_sum;
}
}
}
cout << max_sum << endl;
return 0;
}
但这个的时间复杂度是 O(n^2),我们需要考虑更高效的做法,比如动态规划.
动态规划解决
状态定义:
- 我们定义dp[i]表示以第i个元素结尾的最大连续子序列和,然后通过遍历序列,不断更新这个数组的值。这样,最终结果就是这个数组中的最大值
- dp[i] 与前面的dp[i-1]是什么关系 ?
- 1~i 的和是不是等于 dp[i-1] + nums[i]?
- 有什么特殊问题需要考虑? 负数
- 如果前一个元素的最大连续子序列和是负数,那么当前元素的最优解就是其本身;如果前一个是正数,则当前元素加上前一个的最大值才是最优解。
状态转移方程:
dp[i] = max(a[i], dp[i-1] + a[i])
#include <bits/stdc++.h> // 用于max函数
const int N=2e5;
using namespace std;
int dp[N];
int main() {
int n;
cin >> n;
int a[n];
for(int i=0; i<n; i++) cin >> a[i];
dp[0] = a[0];
int max_sum = dp[0];
for(int i=1; i<n; i++) {
dp[i] = max(a[i], dp[i-1] + a[i]);
max_sum = max(max_sum, dp[i]);
}
cout << max_sum << endl;
return 0;
}