最大子段和之环形问题
环形最大子段和
题目模型
- 把模型一的线性变成环形。有一个修改,不允许区间为空。
问题分析
方法一:
-
环形数组的连续最大子段和,有两种情况。
- 最大和的这个子段没有包含头尾。此时跟线型一样。
- 定义
dp[i]
表示以a[i]
结尾的最大子段和。 - 转移方程:
dp[i]=max(dp[i-1]+a[i],a[i])
。
- 定义
- 最大和的这个子段包含了头尾。
- 此时:最大子段和 = 整个序列和 - 最小子段和。
- 此时最小子段和肯定是不包括头尾的,我们可以把原序列的每个元素乘以
-1
,然后求出最大子段和,即为原序列的不包括头、尾的最小子段和。
- 最大和的这个子段没有包含头尾。此时跟线型一样。
-
然后比较两种情况的大小,输出大的那一个就行。
-
Code
#include <bits/stdc++.h> const int maxn = 1e7+5,Inf=0x3f3f3f3f; typedef long long LL; LL a[maxn],b[maxn],dp[maxn]; LL sum = 0,Max = 0,Min = 0; void Solve(){ int n;scanf("%d",&n); srand(time(0));//随机种子 for(int i=1;i<=n;++i){ a[i]=rand()%10000-5000;//产生-5000~5000的随机数 b[i]=-a[i];//原序列元素乘-1 sum+=a[i];//序列之和 } for(int i=1;i<=n;++i){//对应情况1 dp[i]=std::max(dp[i-1]+a[i],a[i]); Max=std::max(Max,dp[i]); } memset(dp,0,sizeof(dp)); for(int i=1;i<=n;++i){//对应情况2,求b的最大子段和,取反后为a的的最小子段和 dp[i]=std::max(dp[i-1]+b[i],b[i]); Min=std::max(Min,dp[i]); } LL ans=std::max(Max,sum+Min);//sum+Min相当于序列和减去最小区间和 printf("%lld\n",ans); } int main(){ Solve(); return 0; }
方法二:
- 可以用单调队列,具体做法见下一个模型。
hzoi