今天写一道非常经典的题目,对于这道题做法很多,可以用枚举,分治,动态规划,枚举算法求解这类题目一般很难通过,这里我主要讲解后两种算法思想。
题目链接
题目描述
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n。
第二行有 n 个整数,第 i 个整数表示序列的第 i个数字 。
输出格式
输出一行一个整数表示答案。
输入输出样例
输入 #1
7
2 -4 3 -1 2 -4 3
输出 #1
4
说明/提示
样例 1 解释
选取 [3,5] 子段 {3,−1,2},其和为 4。
数据规模与约定
- 对于 40% 的数据,保证。
- 对于 100% 的数据,保证 。
解题思路(分治)
分治的思路很简单,就是将整个问题划分成若干个子问题来求解,当子问题规模较小时可以直接求解,并利用递归算法来求解,最后将子问题的解合并成原问题的解。这里,我们将原问题进行均分对于一个问题,我们将其分成左右两个子段,分别求其最大字段和,最后再比较这两个结果的大小,取最大值即可。
AC代码(分治)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
typedef long long ll;
const int N = 200005;
int a[N];
int maxSumDC(int a[], int low, int high)
{
if (low == high) return a[low];
int mid = (low + high) / 2;
int maxLeft = maxSumDC(a, low, mid);
int maxRight = maxSumDC(a, mid + 1, high);
int s1 = 0, max1 = -1e5, s2 = 0, max2 = -1e5;
for (int i = mid; i >= low; i--) { s1 += a[i]; if (s1 > max1) max1 = s1; }
for (int i = mid + 1; i <= high; i++) { s2 += a[i]; if (s2 > max2) max2 = s2; }
int maxSum = max1 + max2;
if (maxLeft > maxSum) maxSum = maxLeft;
if (maxRight > maxSum) maxSum = maxRight;
return maxSum;
}
signed main()
{
int n;
cin >> n;
memset(a, 0, sizeof a);
for (int i = 1; i <= n; i++) cin >> a[i];
cout<<maxSumDC(a, 1, n);
return 0;
}
解题思路(动态规划)
动态规划的思路也很简单,我们用a数组记录每个数,b数组记录以a[i]结尾的序列的最大字段和。
状态转移:
if (b[i - 1] > 0) b[i] = b[i - 1] + a[i];
else b[i] = a[i];
如果a[i-1]结尾的最大字段和小于或等于0,那么以a[i]结尾的最大字段和就是a[i]即可,否则,就是以a[i-1]结尾的最大字段和加上a[i],同时,我们记录并更新最大值,最后输出即可。
AC代码(动态规划)
signed main()
{
int a[200005],b[200005];
int n;
cin >> n;
memset(a, 0, sizeof a);
memset(b, 0, sizeof b);
for (int i = 1; i <= n; i++) cin >> a[i];
int res = -1e5;
for (int i = 1; i <= n; i++)
{
if (b[i - 1] > 0) b[i] = b[i - 1] + a[i];
else b[i] = a[i];
if (b[i] > res) res = b[i];
}
cout << res;
return 0;
}