四种方法解决最大子列和问题 (三重循环,二重循环,分而治之,在线处理)

题目描述:

7-1 最大子列和问题 (20 分)

给定K个整数组成的序列{ N1​,N2,…,NK },“连续子列”被定义为{ Ni,Ni+1, …,Nj},其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:

数据1:与样例等价,测试基本正确性;
数据2:102个随机整数;
数据3:103个随机整数;
数据4:104个随机整数;
数据5:105个随机整数;

输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。

输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:
6
-2 11 -4 13 -5 -2
(结尾无空行)

输出样例:
20
(结尾无空行)

//第一行输入正整数第二行给出K个整数,其间以空格分隔
#include <iostream>
#define MaxNum 100000
using namespace std;
//1和2:遍历所有元素,计算所有子列的和。暴力遍历
//3二分法 
 
int zuidaziliehe1(int List[],int K)//o(n^3)
{
	int i,j,k;
	int ThisSum=0,MaxSum=0;
	for(i=0;i<K;i++)
	{
	//	ThisSum=0;
		for(j=i;j<K;j++)
		{	
			ThisSum=0;
			for(k=i;k<j;k++)
				ThisSum+=List[k];
			if(ThisSum>MaxSum)
			MaxSum=ThisSum;
		}
	}
	return MaxSum;
}

///
///
int zuidaziliehe2(int List[],int K)//O(n^2)
{
	int i,j,k;
	int ThisSum=0,MaxSum=0;
	for(i=0;i<K;i++)
	{
		ThisSum=0;
		for(j=i;j<K;j++)
		{	
			
			ThisSum+=List[j];
			if(ThisSum>MaxSum)
			MaxSum=ThisSum;
		}
	}
	return MaxSum;
}

///
///
//分而治之 寻找左边与右边部分的最大值,以及中间算起左边与右边的部分 
//最好有左右界限 int left,int right,这样传数组就不需要考虑下标了List[] 
int zuidaziliehe3(int List[],int K)//O(nlogn) 分治法 
{ 
	/*int maxLeftSum=0,maxRightSum=0;
	if(K>1)
	{
		thisSum=0;
		maxLeftSum=zuidaziliehe3(List,K/2);//求左边的最大值 
		maxRightSum=zuidaziliehe3( (List+K/2) , (K-K/2) );
		
		//求中间的最大值
		for(int i=K/2;i<K;i++)
		{
			thisSum+=List[i];
		//	cout<<"i="<<i<<"\t"<<"thisSum="<<thisSum<<endl; 
			if(thisSum>maxSum)
			{
				maxSum=thisSum;
				cout<<maxSum<<" ";
			}
		}
		//求中间的最大值:
		for(int i=mid;i>left;i--)
		{
			thisSum+=List[i];
			if(thisSum>MaxSum)
				MaxSum=thisMax;	
		}n
		for(int i=mid+1;i<right;i++)
		{
			
		}
		thisSum=maxSum;
		for(int i=K/2-1;i>=0;i--)
		{
			thisSum+=List[i];
			if(thisSum>maxSum)
			{
				maxSum=thisSum;
				cout<<maxSum<<" ";
			}
		} 
	}
	else //左右相等 只有一个元素了 
	{K-=1;
	if(*(List+K)<0)return 0;
	else return *(List+K);}
	
	return maxSum;*/
}

//分而治之 寻找左边与右边部分的最大值,以及中间算起左边与右边的部分 
	int Max3( int A, int B, int C )
{ /* 返回3个整数中的最大值 */
    return A > B ? A > C ? A : C : B > C ? B : C;
}

int DivideAndConquer( int List[], int left, int right )
{ /* 分治法求List[left]到List[right]的最大子列和 */
    int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
    int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/

    int LeftBorderSum, RightBorderSum;
    int center, i;

    if( left == right )  { /* 递归的终止条件,子列只有1个数字 */
        if( List[left] > 0 )  return List[left];
        else return 0;
    }

    /* 下面是"分"的过程 */
    center = ( left + right ) / 2; /* 找到中分点 */
    /* 递归求得两边子列的最大和 */
    MaxLeftSum = DivideAndConquer( List, left, center );
    MaxRightSum = DivideAndConquer( List, center+1, right );

    /* 下面求跨分界线的最大子列和 */
    MaxLeftBorderSum = 0; LeftBorderSum = 0;
    for( i=center; i>=left; i-- ) { // 从中线向左扫描  不会对正在加LeftBorderSum的和有改变,一直加;直到LeftBorderSum比max大,更新max 
        LeftBorderSum += List[i];
        if( LeftBorderSum > MaxLeftBorderSum )
            MaxLeftBorderSum = LeftBorderSum;
    } /* 左边扫描结束 */

    MaxRightBorderSum = 0; RightBorderSum = 0;
    for( i=center+1; i<=right; i++ ) { /* 从中线向右扫描 */
        RightBorderSum += List[i];
        if( RightBorderSum > MaxRightBorderSum )
            MaxRightBorderSum = RightBorderSum;
    } /* 右边扫描结束 */

    /* 下面返回"治"的结果 */
    return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}

int MaxSubseqSum3( int List[], int N )
{ /* 保持与前2种算法相同的函数接口 */
    return DivideAndConquer( List, 0, N-1 );
}

///
///
int zuidaziliehe4(int List[],int K)//O(n) 在线处理 
{
	int i=0;
	int ThisSum=0,MaxSum=0;
	/*用于记录当前和与最大和 也是一直加的过程, 
	如果当前项让和变成负的, 从下一个元素开始重新加起来。这时候max没有改变,只是thisMax重新加起来了 */ 
	for(i=0;i<K;i++)
	{
		ThisSum+=List[i];
		if(ThisSum>MaxSum)
			MaxSum=ThisSum;
		else if(ThisSum<0)
			ThisSum=0;
	} 
	return MaxSum;
} 

int main()
{
	int K;
	int a[MaxNum];
	int i=0;
	cin>>K;
	for(i=0;i<K;i++)
	{
		cin>>a[i];
	}
	cout<<zuidaziliehe3(a,K);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分而治之是一种常用的算法思想,可以用来解决最大子列问题。在这种方法中,我们将问题分解成更小的子问题,并在每个子问题上递归地应用该方法。然后,我们将子问题的解合并起来,形成原始问题的解。 下面是使用分而治之方法解决最大子列问题的Python代码: ```python def max_subarray_sum(arr, low, high): # 递归终止条件,当只有一个元素时返回该元素 if low == high: return arr[low] # 分治法的分解步骤,将问题分解为更小的子问题 mid = (low + high) // 2 left_max = max_subarray_sum(arr, low, mid) right_max = max_subarray_sum(arr, mid + 1, high) # 分治法的合并步骤,将子问题的解合并得到原始问题的解 cross_max = max_crossing_sum(arr, low, mid, high) # 返回左子数组最大子列和、右子数组最大子列和和跨越中点的最大子列和中的最大值 return max(left_max, right_max, cross_max) def max_crossing_sum(arr, low, mid, high): # 计算包含中点的左侧最大子列和 left_sum = float("-inf") curr_sum = 0 for i in range(mid, low - 1, -1): curr_sum += arr[i] if curr_sum > left_sum: left_sum = curr_sum # 计算包含中点的右侧最大子列和 right_sum = float("-inf") curr_sum = 0 for i in range(mid + 1, high + 1): curr_sum += arr[i] if curr_sum > right_sum: right_sum = curr_sum # 返回左侧最大子列和、右侧最大子列和和跨越中点的最大子列和的和 return left_sum + right_sum # 测试代码 arr = [-2, 11, -4, 13, -5, -2] max_sum = max_subarray_sum(arr, 0, len(arr) - 1) print(max_sum) ``` 这段代码通过递归地将问题分解为更小的子问题,并在每个子问题上应用该方法。然后,它将子问题的解合并起来,得到原始问题的解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值