假定一个小白看这道题,一步步了解相关知识
状态dp[i]
表示以A[i]
作结尾的最大连续子序列和,求出dp
数组的最大值即题解
dp[1] = a[1]; //边界条件
for(int i = 2; i <= n; ++i)
{
dp[i] = max(dp[i - 1] + a[i], a[i]); //状态转移方程
}
sort(dp + 1, dp + n + 1); //找到dp数组的最大值
ans1 = dp[n];
即a[n]
和a[1]
相邻,取得的最大子段分两种情况
a[n]
和a[1]
,即和非环状最大子段和的结果是一样的,直接使用DP可解,得ans1
a[n]
和a[1]
,此时,问题转化为求最小子段和min1
,然后我们求整个序列的和sum
,得ans2 = sum - min1
ans1
和ans2
的最大值就是题解
有些初学者难以理解ans2 = sum - min1
,它的前提条件是序列是一个环状,这个环上的数字的总和是确定的,我们从这个环上剔除和最小的子段,剩下子段的和自然就是最大子段和(这里,我们需要清楚,最大子段和最小子段只有一个可以跨过序列首和尾的,因为这个转折点只有一个),举个栗子
这个序列的和sum = 9
,最小序列(未跨)和是min = a[2] + a[3] = -7
,那么最大序列(跨过)和就是ans2 = sum - min = 16 = a[4] + a[5] + a[1]
和求最大子段和类似,状态dp[i]
表示以a[i]
结尾的最小子段和,分两种情况求dp[i]
如果dp[i - 1] > 0
那么a[i]
就自立门户,dp[i] = a[i]
否则,就将a[i]
接在a[i - 1]
后面,dp[i] = a[i] + dp[i - 1]
下面是利用最小子段和求环状最大子段和
dp[1] = a[1]; //边界条件
sum = a[1];
for(int i = 2; i <= n; ++i)
{
dp[i] = min(a[i], dp[i - 1] + a[i]); //状态转移方程
sum += a[i];
}
sort(dp + 1, dp + n + 1);
if(dp[1] < 0)
min1 = dp[1]; //找到dp数组最小值
else
min1 = 0;
ans2 = sum - min1; //求得ans2
cout << max(ans1, ans2);