您的当前位置:首页正文

P1121环状最大两段子段和

2024-11-27 来源:个人技术集锦

题目

假定一个小白看这道题,一步步了解相关知识

最大子段和(DP,O(n))

状态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

ans1ans2的最大值就是题解

有些初学者难以理解ans2 = sum - min1,它的前提条件是序列是一个环状,这个环上的数字的总和是确定的,我们从这个环上剔除和最小的子段,剩下子段的和自然就是最大子段和(这里,我们需要清楚,最大子段和最小子段只有一个可以跨过序列首和尾的,因为这个转折点只有一个),举个栗子

这个序列的和sum = 9,最小序列(未跨)和是min = a[2] + a[3] = -7,那么最大序列(跨过)和就是ans2 = sum - min = 16 = a[4] + a[5] + a[1]

最小(负数)子段和(DP,O(n))

和求最大子段和类似,状态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);

显示全文