3.4 最大字段和



1.什么是最大子段和?

我的理解:

最大子段和是一个经典的问题,也称为最大子数组和问题。给定一个整数数组,要求找到数组中连续子数组的和的最大值。这个连续子数组可以为空,但其和必须是数组中所有连续子数组中最大的。

形式化地说,对于给定的整数数组 A,最大子段和问题可以表示为:找到数组 A 中的两个索引 i 和 j(其中 0 ≤ i ≤ j < n),使得子数组 A[i:j] 的元素之和最大。

例如,对于数组 A = [-2, 1, -3, 4, -1, 2, 1, -5, 4],最大子段和为子数组 [4, -1, 2, 1],其和为 6。

解决最大子段和问题的常用算法是动态规划算法,具体的思路是通过迭代更新子数组的和,同时记录最大值。该算法的时间复杂度为 O(n),其中 n 是数组的长度。

 2.最大子段和问题的简单算法

源代码:

int MaxSum(int n,int *a,int& besti,int& bestj)
{
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<n;j++)
		{
			int thissum=0;
			for(int k=i;k<=j;k++)
			{
				thissum+=a[k];
				if(thissum>sum)
				{
					besti=i;
					bestj=j;
				}
			}
		 } 
	}
	return sum;
}

代码思路:

我的理解:

这段代码是一个函数定义,它计算一个数组中连续子数组的最大和,并通过传引用的方式返回最大和对应的子数组的起始和结束索引。

代码的主要部分是三个嵌套的循环。外层的第一个循环迭代变量i用于确定子数组的起始索引,它从1开始遍历到n。内层的第二个循环迭代变量j用于确定子数组的结束索引,它从i开始遍历到n-1。这样的设计是为了考虑所有可能的子数组。

在最内层的第三个循环中,变量k从i遍历到j,用于计算当前子数组的和,即thissum。在每次计算完thissum后,会检查是否该子数组的和大于之前记录的最大和sum。如果是,则更新最大和sum,并记录当前子数组的起始索引besti和结束索引bestj。

最后,函数返回的是sum,即最大和。

然而,这段代码存在一个问题:没有在找到更大的子数组和时更新sum的值。解决方法是在 if (thissum > sum) 的条件判断中添加 sum = thissum; 的语句。这样才能确保函数返回的是正确的最大和。

 逐行解释:

好的,以下是对每一行代码的逐行解释:

第 5 行:定义了一个整数变量 `sum`,用于存储最大和的值。初始值设为 0。

第 7 行:外层循环的起始,迭代变量 `i` 从 1 开始,遍历到 `n`。

第 9 行:内层循环的起始,迭代变量 `j` 从 `i` 开始,遍历到 `n-1`。

第 11 行:定义了一个整数变量 `thissum`,用于存储当前子数组的和。初始值设为 0。

第 13 行:最内层循环的起始,迭代变量 `k` 从 `i` 开始,遍历到 `j`。

第 15 行:将数组中第 `k` 个元素的值加到 `thissum` 上,累计计算子数组的和。

第 17-19 行:如果 `thissum` 大于之前记录的最大和 `sum`,则更新最大和对应的子数组的起始索引 `besti` 和结束索引 `bestj`。

第 23 行:函数返回变量 `sum`,即最大和。

这样,通过逐行解释,你可以更好地理解代码的逻辑和功能。

3.最大子段和问题得分治算法 

int MaxSubSUm(int *a,int left,int right)
{
	int sum=0;
	if(left==right)
	{
		sum=a[left]>0?a[left]:0;
	}
	else
	{
		int center=(left+right)/2;
		int leftsum=MaxSubSum(a,left,center);
		int rightsum=MaxSubSum(a,center+1,right);
		int s1=0;
		int lefts=0;
		for(int i=center;i>=left;i--)
		{
			lefts+=a[i];
			if(lefts>s1)
			{
				s1=lefts;
			}
			int s2=0;
			int rights=0;
			for(int i=center+1;i<=right;i++)
			{
				right+=a[i];
				if(rights>s2)
				{
					s2=rights;	
				} 
				sum=s1+s2;
				if(sum<leftsum)
				{
					sum=leftsum;
				}
				if(sum<rightsum)
				{
					sum=rightsum;
				}
			}
			return sum;
			
		 }
		 int MaxSum(int n,int *)
		 {
		 	return MaxSubSum(a,1,n);	
		 } 
	}
}

 我对这段代码的理解:

代码的功能是计算给定数组的最大子数组和。它使用了分治法的思想,将数组划分为左右两个子数组,分别求解左右子数组的最大子数组和,然后计算包含中间元素的最大子数组和。

具体解释如下:

  • 第 3 行:定义一个整数变量 sum,用于存储最大子数组和的值,初始值为 0。
  • 第 4-7 行:如果 leftright 相等,说明只有一个元素,将 sum 设置为该元素的值(如果大于 0),否则设置为 0。
  • 第 9-15 行:如果有多个元素,则计算数组的中间索引 center
  • 第 16-17 行:递归调用 MaxSubSum 函数,分别求解左右两个子数组的最大子数组和。
  • 第 18-23 行:初始化变量 s1lefts,并从中间元素向左遍历,累计计算左半部分的最大子数组和。
  • 第 24-30 行:初始化变量 s2rights,并从中间元素的右侧开始向右遍

三目运算符:

 

三目运算符(也称为条件运算符)是一种在条件为真或假时选择不同操作的简洁方式。它的语法形式如下:
条件表达式 ? 表达式1 : 表达式2

其中,条件表达式的结果为真(非零值)或假(零值)。如果条件为真,整个表达式的结果为表达式1的值;如果条件为假,整个表达式的结果为表达式2的值。

以下是一个示例,说明如何使用三目运算符:
int a = 10;
int b = 5;
int max = (a > b) ? a : b;

在上述示例中,条件表达式 `(a > b)` 判断变量 `a` 是否大于变量 `b`。如果条件为真,将返回变量 `a` 的值;如果条件为假,将返回变量 `b` 的值。因此,变量 `max` 将被赋值为 `a` 的值(即 10)。

三目运算符可以简化某些条件下的逻辑判断和赋值操作,使代码更加简洁和易读。然而,使用时要注意不要过度使用或使代码难以理解。在某些情况下,使用常规的 `if-else` 语句可能更加清晰和易于维护。

 

4.最大子段和问题与动态规划算法的推广 

最大子段和问题是最大子数组和问题的一个推广。在最大子段和问题中,我们考虑的是连续子段的和,而不仅仅是连续子数组的和。

给定一个整数数组 `a`,最大子段和问题的目标是找到具有最大和的连续子段,并返回该子段的和。

动态规划算法可以推广到解决最大子段和问题。以下是基于动态规划的最大子段和问题的算法思路:

1. 定义状态:我们使用一个一维数组 `dp` 来记录以每个元素结尾的最大子段和。`dp[i]` 表示以第 `i` 个元素结尾的最大子段和。
2. 初始化状态:将 `dp[0]` 初始化为数组的第一个元素 `a[0]`。
3. 状态转移:对于每个元素 `a[i]`,我们有两种选择:
   - 要么将其加入之前的子段中,即 `dp[i] = dp[i-1] + a[i]`;
   - 要么从当前元素开始重新计算子段和,即 `dp[i] = a[i]`。
   我们选择较大的那个作为 `dp[i]` 的值,表示包含第 `i` 个元素的最大子段和。
4. 最终结果:遍历所有的 `dp[i]`,取其中的最大值,即为最大子段和。

这个算法与最大子数组和问题的动态规划算法非常类似,唯一的区别是定义状态的含义。最大子数组和问题关注的是连续子数组的和,而最大子段和问题关注的是连续子段的和。

这个推广后的动态规划算法仍然具有时间复杂度为 O(n),其中 n 是数组的长度。通过动态规划,我们可以有效地解决最大子段和问题,并找到具有最大和的连续子段。

 

5.最大子段和问题与动态规划算法的推广 


int MaxSum(int m, int n, int **a)
{
    int sum = 0;  // 初始化最大子矩阵和为0
    int *b = new int[n+1];  // 创建一个辅助数组b,用于计算子矩阵的元素和
    
    for (int i = 1; i <= m; i++)  // 遍历行
    {
        for (int k = 1; k <= n; k++)  // 将辅助数组b初始化为0
        {
            b[k] = 0;
        }
        
        for (int j = i; j <= m; j++)  // 遍历子矩阵的下边界
        {
            for (int k = 1; k <= n; k++)  // 遍历每列,将元素累加到辅助数组b中
            {
                b[k] += a[j][k];
            }
            
            int max = MaxSum(n, b);  // 调用递归函数MaxSum,计算辅助数组b的最大子数组和
            if (max > sum)  // 如果得到的最大子数组和大于当前最大子矩阵和,则更新最大子矩阵和
            {
                sum = max;
            }
        }
    }
    
    return sum;  // 返回最大子矩阵和
}

 

 

 

总结:

当涉及到最大子段和问题时,可以使用动态规划算法来有效地解决。以下是最大子段和问题的动态规划算法的要点总结:

1. 定义状态:我们可以使用一个一维数组 `dp` 来记录以每个位置结尾的最大子段和。`dp[i]` 表示以第 `i` 个位置结尾的最大子段和。
2. 初始化状态:将 `dp[0]` 初始化为数组的第一个元素 `a[0]`。
3. 状态转移:对于每个位置 `i`,我们有两种选择:
   - 要么将其加入之前的子段中,即 `dp[i] = dp[i-1] + a[i]`;
   - 要么从当前位置重新开始计算子段和,即 `dp[i] = a[i]`。
   我们选择较大的那个作为 `dp[i]` 的值,表示包含第 `i` 个位置的最大子段和。
4. 最终结果:遍历所有的 `dp[i]`,取其中的最大值,即为最大子段和。

根据上述要点,以下是最大子段和问题的动态规划算法的伪代码:

```plaintext
Initialize dp[0] = a[0]

for i from 1 to n-1 do
    dp[i] = max(dp[i-1] + a[i], a[i])

result = max(dp[0], dp[1], ..., dp[n-1])
```

其中,`n` 是数组的长度,`a` 是给定的数组。

通过动态规划算法,我们可以有效地计算出最大子段和,并找到具有最大和的连续子段。该算法的时间复杂度为 O(n),其中 n 是数组的长度。动态规划算法通过利用子问题的最优解来构建整体问题的最优解,解决了最大子段和问题的复杂性。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值