分治策略
简单地来说,就是递归地求解一个问题,在每层递归中应用如下三个步骤:
分解:将问题划分为一些子问题。子问题的形式与原问题一样,只是规模更小。
解决:递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解。
合并:将子问题的解组合为原问题的解。
注意:有时,除了与原问题形式完全一样的规模更小的子问题外,还需要求解与原问题不完全一样的子问题。此时,将这些子问题的求解看作合并步骤的一部分。
最大子问题的问题描述
问题:输入一个整形数组(有正数也有负数),数组中连续的、一个或多个元素组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。
输入:测试数组13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7
输出:最大子数组为3, 10, -4, 7, 2;
输出最大子数组的和为18 。
暴力求解方法
先介绍一种暴力求解方法,此方法时间复杂度较高,但设计简单, 只要列出数组所有可能的组合,然后找出其中和最大的组合即可暴力求解方法分三层循环实现:
(1)第一层循环用于固定子数组的起始位置。
(2)第二层循环用于确定子数组的结束位置。
(3)第三层循环用于子数组和的计算,从子数组的头开始遍历到其尾,累加起来就是该子数组的和。
int ViolenceMax(int *arry, int n, int &start, int &end)
{
int i, j, k;
int sum;//用于求和
int max = -1000000;//记录最大和
for (i = 0; i<n; i++)
{
for (j = i; j<n; j++)
{
sum = 0;
for (k = i; k<j; k++)
{
sum += arry[k];
}
if (sum>max)
{
start = i;//子数组开始处
end = j;//子数组结尾处
max = sum;//所求解的最大子数组的和
}
}
}
return max;//返回求解的最大子数组的和
}
int main()
{
int a[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int i, j;
int max = ViolenceMax(a, 16,i, j);
cout <<"最大子数组的和" <<max << endl;
cout << "最大子数组范围:" << "[" << i << "," << j << ")" << endl;
system("pause");
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/284d54be2d93a03e12eb10a534abb600.png)
分治策略求解方法
假定我们要寻找子数组A[low...high]的最大子数组。使用分治技术意味着要将子数组划分为两个规模尽量相等的子数组。其实就是指子数组的中间位置mid,然后考虑求解两个子数组A[low...mid]和A[MID...high]。所以,最大子数组A[i..j]的情况必然是下面三种情况之一:(1)完全位于子数组A[low...mid]中,因此low<=i<=j<=mid。
(2)完全位于子数组A[mid+1...high]中,因此mid<i<=j<=high。
(3)跨越了中点,因此low<=i<=mid<j<=high。
int MiddleMax(int *arry, int l, int r, int m);
int Divide(int *arry, int l, int r)
{
if (l == r)//只有一个元素时,返回该元素
return arry[l];
else
{
int m = (l + r) / 2;
int l_max = -1000, r_max = -1000, m_max = -1000;
l_max = Divide(arry, l, m);//左边和的最大值
r_max = Divide(arry, m + 1, r);//右边和的最大值
m_max = MiddleMax(arry, l, r, m);//中间和的最大值
//返回三个值中最大的一个
if (l_max >= r_max && l_max >= m_max)
return l_max;
else if (r_max >= l_max && r_max >= m_max)
return r_max;
else
return m_max;
}
}
下面是求解跨越了中点的最大子数组算法,固定住左右边界,然后在这个范围内寻找
int MiddleMax(int *arry, int l, int r, int m)
{
int l_max = -1000, r_max = -1000;//分别用于记录左、右方向累加的最大和
int i;
int sum;//用于求和
sum = 0;
for (i = m; i >= l; i--)//中线开始向左寻找
{
sum += arry[i];
if (sum>l_max)
l_max = sum;
}
sum = 0;
for (i = m + 1; i<r; i++)//中线开始向右寻找
{
sum += arry[i];
if (sum>r_max)
r_max = sum;
}
return (l_max + r_max);//返回左右之和
}
int main()
{
int a[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};//16
int i=0, j=15, max;
max=Divide(a, i, j);
cout <<"最大子数组的和" <<max << endl;
system("pause");
return 0;
}