(1)题目
来源
题目描述
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n。
第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai。
输出格式
输出一行一个整数表示答案。
输入样例
7
2 -4 3 -1 2 -4 3
输出样例
4
说明/提示
样例 1 解释
选取 [3,5]子段 {3,−1,2},其和为 4。
数据规模与约定
对于 40% 的数据,保证 n≤2×10^3。
对于 100% 的数据,保证 1≤n≤2×10^5,−10^4≤ai≤10^4。
(2)思路
1. 分析问题:分析已知和未知
首先,说明一下,我们讨论这个问题要经历以下步骤:
a. 枚举子段(头-尾)→b. 枚举子段(头-长度)→c. 前缀和(尾和-前头和)→d. 动态规划→e. 简化(去数组)
a. b. c. 步骤只能得40分,会有3个测试点不过(因为超时了),注意一下,开数组时开大一点,1≤n≤2×10^5
a. 枚举子段(头-尾)=>40分
枚举所有子段的头和尾,求出每个子段和,与maxi比较
注:max函数使用方法=> a=max(a,b) =>a等于a(原来)与b中最大的数
sum用于计算子段和
#include<iostream>
using namespace std;
int main()
{
int n,a[200005],maxi=-100000005,sum=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)//头
{
for(int j=i;j<=n;j++)//尾
{
sum=0;
for(int k=i;k<=j;k++) sum+=a[k];//子段和
maxi=max(maxi,sum);
}
}
cout<<maxi;
return 0;
}
b. 枚举子段(头-长度)=>40分
这里有2点要注意一下:
枚举长度时是从1到(n+1-j),长度会随着头的变化而变化;
求子段和时,应是从i(头)到(i+j-1),因为长度中包括子段的尾。
#include<iostream>
using namespace std;
int main()
{
int n,a[200005],maxi=-100000005,sum=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)//头
{
for(int j=1;j<=n+1-i;j++)//长度
{
sum=0;
for(int k=i;k<=i+j-1;k++) sum+=a[k];//子段和
maxi=max(maxi,sum);
}
}
cout<<maxi;
return 0;
}
c. 前缀和(尾和-前头和)=>40分
前缀和通俗点来讲就是数组或数列中,从头的前一个数到这个数所有数字之和(包括头和这个数字)
通过前缀和,也可以找出每个子段,并求和即比较
注:从头的前一个数到这个数,而不是从头开始,是因为头的前缀和中包含头的数字,从头开始则把头的数字减掉了
#include<iostream>
using namespace std;
int a[200005],f[200005];
int main()
{
int n,maxi=-100000005,sum=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)//前缀和
{
f[i]=f[i-1]+a[i];
}
for(int i=1;i<=n;i++)//头
{
sum=0;
for(int j=i;j<=n;j++)//尾
{
sum=f[j]-f[i-1];//尾和-头和
maxi=max(maxi,sum);
}
}
cout<<maxi;
return 0;
}
d. 动态规划=>100分
f[i]:从第1位到第 i 位中最大子段和
求出每个f[i],但f[1]=a[1];
再比较产生的新的子段和与前一个最大子段和,但通过下图可得,若f[i]+a[i]>f[i],f[i]=f[i]=a[i],若f[i]+a[i]<f[i],f[i]不变,这样可以减少时间复杂度;
最后比较maxi与f[i];
画图如下(数据为样例):
#include<iostream>
using namespace std;
int a[200005],f[200005];
int main()
{
int n,maxi=-100000000;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
f[1]=a[1];
for(int i=2;i<=n;i++)
{
f[i]=max(f[i-1]+a[i],a[i]);
maxi=max(maxi,f[i]);
}
cout<<maxi;
return 0;
}
e. 简化(去数组)=>100分
我们可以把动态规划的内容转换成不带数组的形式,如下
f只要这一位和前一位,根据算法,前一位的f可保留
a只用这一位就够了
所以f和a可以去数组
#include<iostream>
using namespace std;
int main()
{
int n,a,f=0,maxi=-1000000000;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
if(f+a>a) f=f+a;
else f=a;
maxi=max(maxi,f);
}
cout<<maxi;
return 0;
}
2. 数据定义:已知和未知的取名和类型
n:序列长度
a:输入的数
f:从第1位到第 i 位中最大子段和
maxi=-1000000000:−10^4≤ai≤10^4,maxi范围较大,从-10^9开始
3. 数据输入:输入已知
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;}
4. 数据计算:数字建模+设计算法
代码较短,前面也已经分析的差不多了,就不写了
(3)完整AC代码
#include<iostream>
using namespace std;
int main()
{
int n,a,f=0,maxi=-1000000000;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
if(f+a>a) f=f+a;
else f=a;
maxi=max(maxi,f);
}
cout<<maxi;
return 0;
}