题目链接
题目描述与示例
一个数组有 N 个元素,求连续子数组的最大和。 例如:[-1,2,1],和最大的连续子数组为[2,1],其和为 3
输入描述
输入为两行。 第一行一个整数n(1 <= n <= 100000),表示一共有n个元素 第二行为n个数,即每个元素,每个整数都在32位int范围内。以空格分隔。
输出描述
所有连续子数组中和最大的值。
示例
输入:
3 -1 2 1
输出:
3
解题思路
题目分析
题目还是比较简短,还是有点小挑战性,就是给定一个数组,判断哪个连续子数组的和最大
个人思路
本来我在想没有做出来的题还要不要写自己的思路,但是吧,自己想出来的东西怎么能嫌弃呢是吧,所以决定还是写写自己的思路吧
- 定义两个vector数组num1和num2分别存放一段区间的正数和以及负数和
- 因为数组是连续的,合并num1和num2后一定是一正一负
- 到这里就有点迷糊了,本来想的是正+负的最大即可,但是忽略了正+负后面还有个正,但是又+上正数后说不定后面的负+正>0那么就又可以加进来
- 最后还是没有捋顺提交了
代码展示
写在前面:
本次代码没有通过,不具备参考性,纯属为了记录一下
#include<iostream>
#include<vector>
using namespace std;
int Sum(vector<int> A,size_t& begin)
{
int sum=0;
if(A[begin]>=0)
{
for(;begin<A.size();begin++)
{
if(A[begin]>=0)
sum+=A[begin];
else
{
begin--;
return sum;
}
}
}
if(A[begin]<0)
{
for(;begin<A.size();begin++)
{
if(A[begin]<0)
sum+=A[begin];
else
{
begin--;
return sum;
}
}
}
return sum;
}
int main()
{
int n;
vector<int> arr;
vector<int> sum1;//保存一段区间的正数和
vector<int> sum2;//保存一段区间的负数和
//sum1的第一个位置保存第一段整数和
//后面的sum1和sum2位置一正一负如果和大于0就添加到sum1[0]
cin>>n;
arr.resize(n);
for(size_t i=0;i<arr.size();i++)
{
cin>>arr[i];
}
for(size_t i=0;i<arr.size();i++)
{
if(arr[i]>=0)
{
int tmp=Sum(arr,i);
sum1.push_back(tmp);
}
if(arr[i]<0)
{
int tmp=Sum(arr,i);
sum2.push_back(tmp);
}
}
//到这sum1全是正数和,sum2除第一个元素全是负数和
//要想和最大,第一个数一定从sum1取,且sum1和sum2一一对应
int max=0;
for(size_t i=0;i<sum1.size();i++)
{
int all=sum1[i];
for(size_t j=i;j<sum1.size();j++)
{
if(sum1[j+1]+sum2[j]>0)
{
all+=sum1[j+1]+sum2[j];
}
else
{
if(all>max)
max=all;
continue;
}
}
}
cout<<max;
return 0;
}
题解
看了题解才知道这道题的处理方法大致上分两种:
暴力求解:双层循环,i从0-n-1,j从i+1-n-1,定义一个sum和max,sum每次+j后与max判断,如果大于max则max=sum。这样下来即可求解。----这个思路还是比较简单的,不过我却没有想到
DP(Dynamic Programming)求解:
状态方程式: max( dp[ i ] ) = getMax( max( dp[ i -1 ] ) + arr[ i ] ,arr[ i ] )
dp[i]
就是以数组下标为i
的数做为结尾的最大子序列和,注意是以i
为结尾,比如说现在有一个数组{6,-3,-2,7,-15,1,2,2},dp[2]就是以-2为结尾的,那么显然dp[2]的最大值就是1(6,-3,-2),dp[3]要以7结尾那么以7结尾的子序列最大和就是8(6,-3,-2,7)。现在我们开始细细品一下上面这个递推式,求dp[i]的时候是不是有两种可能,要么就是像上面的dp[3]一样,dp[2]求出来是1了,再加上自己array[3]是最大的,那么还有一种可能就是说如果dp[2]我求出来是-100,那如果我也是dp[2]+array[3]的话是-93,这时候dp[2]反而是累赘,最大就是自己(因为前面定义了必须以i为结尾,也就说必须以7结尾)。
因为暴力求解的思路比较简单且实现也比较容易,这里着重给出DP的代码:
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n;
cin >> n;
vector<int> v(n);
for(int i = 0; i < n; ++i){
cin >> v[i];
}
int sum = v[0];
int maxsum = v[0];
for(int i = 1; i < n; ++i){
sum = sum > 0 ? sum + v[i] : v[i];
maxsum = max(sum, maxsum);
}
cout << maxsum;
return 0;
}
以上就是本题的常规题解了,下面介绍一个巧解,还是先上代码:
#include <iostream>
using namespace std;
int main(){
int n,curSum=0,maxSum=-1e5;
cin>>n;
int arr[n];
for(int i=0;i<n;i++){
cin>>arr[i];
curSum+=arr[i];
if(curSum>maxSum){
maxSum=curSum;
}
if(curSum<0){
curSum=0;
}
}
cout<<maxSum<<endl;
return 0;
}
原理的话我有点不太好描述,这里画个图讲解:
前面应该都还是挺好理解的,至于为什么当curSum小于0的时候要归零,实际上当curSum小于0的时候,因为后面还要执行+=arr[i],一个求最大值的问题中加上一个负数还不如不加,简单理解就是说如果-7的时候不归零再加上1,还不如直接舍弃-7取1来的大。