目录
一.利用分治算法求解最大子数组
#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] 的所有子数组的最大连续和。但是实际的编程中我们只需要存储所有最大连续和中的最大值,就不用数组存储了,用变量就足够了