利用分治算法求解最大子数组,利用DP算法(动态规划)求解最大子数组

文章介绍了两种算法来求解数组中的最大子数组和:一是使用分治策略,通过递归计算左右及跨中点的最大子数组,然后进行比较;二是应用动态规划,维护过程量和状态量来追踪最大子数组的起点、终点和和。这两种方法都是在寻找数组中的连续子序列,使得其和最大。
摘要由CSDN通过智能技术生成

目录

一.利用分治算法求解最大子数组

 二.利用DP算法(动态规划)求解最大子数组


一.利用分治算法求解最大子数组

#include<iostream>
#include<limits.h> //后续用到 INT_MIN
using namespace std;

struct max_data
{
	int max_left;
	int max_right;
	int max_sum;
};

max_data Find_cross_max(int a[], int low, int mid, int high)
{
	max_data data1;
	int left_sum = INT_MIN;
	int sum = 0;
	for (int i = mid; i >= low; i--) //从mid开始递减,得到[i,mid]的最大子数组
	{
		sum = sum + a[i];
		if (sum > left_sum)
		{
			left_sum = sum;
			data1.max_left = i;
		}
	}

	int right_sum = INT_MIN;
	sum = 0;
	for (int i = mid + 1; i <= high; i++) //从mid+1开始递增,得到[mid+1,i]的最大子数组
	{
		sum = sum + a[i];
		if (sum > right_sum)
		{
			right_sum = sum;
			data1.max_right = i;
		}
	}

	data1.max_sum = right_sum + left_sum;
	return data1;
}

max_data Find_total_max(int a[], int low, int high)
{
	max_data data2;
	//递归出口
	if (high == low)
	{
		data2.max_left = low;
		data2.max_right = high;
		data2.max_sum = a[high];

		return data2;
	}
	//通过递归完成
	else
	{
		int mid = (low + high) / 2;
		//求左端的最大子数组
		max_data maxl_data = Find_total_max(a, low, mid); //利用下面的比较来得到左端的最大子数组
		//求右端的最大子数组
		max_data maxr_data = Find_total_max(a, mid + 1,high); //利用下面的比较来得到右端的最大子数组
		//求整段的最大子数组
		max_data maxc_data = Find_cross_max(a, low, mid, high);

		//比较得到三段最大子数组中的最大子数组
		if (maxl_data.max_sum > maxr_data.max_sum > maxc_data.max_sum)
		{
			return maxl_data;
		}
		else if (maxr_data.max_sum > maxl_data.max_sum > maxc_data.max_sum)
		{
			return maxr_data;
		}
		else
		{
			return maxc_data;
		}
	}
}

int main()
{
	int a1[10] = { 5,-9,12,19,-25,3,20,13,-9,-18 };
	max_data final_data = Find_total_max(a1, 0, 9);
	cout << "left_position: " << final_data.max_left << " " << "right_position: " << final_data.max_right << endl;
	cout << "max_sum: " << final_data.max_sum << endl;

	system("pause");
	return 0;
}

知识点:

1.基本思路:

① 求出完全位于A[low,mid]中的左端最大子数组

② 求出完全位于A[mid+1,high]中的右端最大子数组

③ 求出跨越中点的全局最大子数组A[i,j],其中i在[low,mid]中,j在[mid+1,high]中

④ 比较三个最大子数组的值,得到值最大的最大子数组

2.伪代码:

 求解跨越中点的最大子数组

 通过递归得到最终的最大子数组

  

3.性能分析

 二.利用DP算法(动态规划)求解最大子数组

#include<iostream>
using namespace std;

struct max_data
{
	int max_start;
	int max_end;
	int max_sum;
};

max_data Find_max_array(int a[],int size)
{
	int dp = a[0]; //过程量,可以判断最大子数组的首端
	int res = a[0]; //状态量,用于保存每个阶段的子数组总和的最大值,可以判断最大子数组的尾端
	int start[8] = { 0,0,0,0,0,0,0,0 };
	int end = 0;
	int counts = 1; //用于记录start数组中储存了多少个start值
	int j = 1; //标记
	for (int i = 1; i < size; i++)
	{
		if (dp >= 0)
		{
			dp += a[i]; //如果dp值大于0,则继续加上下一个元素的值,最大子数组的首端不变
		}
		else if (dp < 0)
		{
			dp = a[i]; //如果dp值小于0,则更新最大子数组的首端为下一个元素的位置
			start[j] = i;
			j++;
			counts++;
		}
		if (dp > res) //如果此时的dp值已经大于了res值,则更新res的值以用于后续的判断
		{
			res = dp;
			end = i;
		}
	}

	int max = 0;

	//只有一个end值,所以将start数组中所有大于end的值设置为0(即为消除掉)
	for (int i = 0; i < counts; i++)
	{
		if (start[i] > end)
		{
			start[i] = 0;
		}
	}

	//在剩下的start值中,最大的start值即为最大子数组的开端(因为只有当dp<0时才会更新start的值,而构成dp小于0的前部分数组不会组成最大子数组,因此此时最大的start值即为最大子数组的首位置)
	for (int i = 0; i < counts; i++)
	{
		if (start[i] > max)
		{
			max = start[i];
		}
	}

	max_data data1;
	data1.max_start = max;
	data1.max_end = end;
	data1.max_sum = res;
	return data1;
}

int main()
{
	int a[8] = { 24,-23,25,-7,-12,-5,-62,-50};
	max_data data1 = Find_max_array(a, 8);
	cout << data1.max_start << " " << data1.max_end << " " << data1.max_sum << endl;
}

知识点:

1.动态规划原理:把多阶段决策过程转化为一系列的单阶段决策问题,利用各个阶段之间的递推关系,逐个确定每个阶段的最优化决策,最终堆叠出多阶段决策的最优化决策结果。

2.动态规划本质上就是递归,动态规划通过将问题划分为若干个子问题,通过递推关系,一步一步求得最终问题。

3.eg:

现有一数组 A = <-23, 18, 20, -7, 12, -5, -22> ,设最大连续和问题为状态,即 dp[i] 表示最后一个元素是A[i] 的数组的最大连续和,则有如下推导:

① dp[1] = A[1] = -23 (边界条件,只有一个元素的数组的连续最大和就等于该元素)

② dp[2] = max( dp[1] + A[2], A[2] ) = 18 (递归定义最优解 dp 的值)

③ dp[3] = max( dp[2] + A[3], A[3] ) =  38

④ dp[4] = max( dp[3] + A[4], A[4] ) = 31

⑤ dp[5] = max( dp[4] + A[5], A[5] ) = 43

⑥ dp[6] = max( dp[5] + A[6], A[6] ) = 38

⑦ dp[7] = max( dp[6] + A[7], A[7] ) = 16

...

⑧ dp[i] = max( dp[i-1] + A[i], A[i] ) (状态转移方程)

通过状态转移方程,我们可以得到数组 A[1..i] 的所有子数组的最大连续和。但是实际的编程中我们只需要存储所有最大连续和中的最大值,就不用数组存储了,用变量就足够了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值