运用分治法和动态规划方法求解最大子数组问题
问题描述
给定整数序列
a1,a2,...,an
,求该序列形如
∑jk=iak
子序列和的最大值:
问题求解
分治算法
运用分治法求解最大子段和问题。将原序列划分为两个规模尽量相等的子序列,设划分的中间位置为 mid,则原问题最大子段和包含的序列必然为下列 3 种情况之一:
左侧:完全位于序列
a1,...,amid
中
右侧:完全位于序列
amid+1,...,an
中
跨越两侧:位于序列
ai,...,aj
中,其中
1≤i≤mid≤j≤n
那么最大子段和即为这三个区间的最大子段和的最大值。因此原问题可化为两个求解规模为 n/2 的子问题,和求解一个跨越中点的序列的最大子段和问题。其中第三种情况可分别求解序列
a1,...,amid
和
amid+1,...,an
的最大子序列,然后将两者合并即可,因此该部分的时间复杂度为 Θ(n)。算法的时间复杂度可用递归的形式表示为:
可得,算法的复杂度为 T(n)=Θ(nlgn) 。算法实现如下:
代码实现
struct subarray
{
int low;
int high;
int sum;
};
/***********find the max array in the interval [i...mid...j]***********/
subarray maxCrossArray(vector<int> data, int low, int mid, int high)
{
subarray sa;
int sumLeft = -INT_MAX;
int sum = 0;
for (int i = mid; i > low-1; i--)
{
sum = sum + data[i];
if (sum > sumLeft)
{
sumLeft = sum;
sa.low = i;
}
}
sum = 0;
int sumRifgt = -INT_MAX;
for (int j = mid + 1; j < high+1; j++)
{
sum = sum + data[j];
if (sum > sumRifgt)
{
sumRifgt = sum;
sa.high = j;
}
}
sa.sum = sumLeft + sumRifgt;
return sa;
}
/**********Divide and conquer algorithm**********/
subarray find(vector<int> data, int low, int high)
{
if (data.empty())
{
cout << "please check the input array is empty!" << endl;
exit(-1);
}
else
{
subarray sa, saLeft, saRight, saCross;
if (high == low)
{
sa.high = high;
sa.low = low;
sa.sum = data[low];
return sa;
}
else
{
int mid = floor((low + high) / 2);
saLeft = find(data, low, mid);
saRight = find(data, mid + 1, high);
saCross = maxCrossArray(data, low, mid, high);
if (saLeft.sum >= saRight.sum && saLeft.sum >= saCross.sum)
return saLeft;
else if (saRight.sum >= saLeft.sum && saRight.sum >= saCross.sum)
return saRight;
else
return saCross;
}
}
}
动态规划算法
最大子段和问题具有最优子结构性质。取规模为n的整数序列的子结构为 a1,...,an−1 ,则原序列的最大子段和包含的序列要么是子结构的最大字段和序列,要么是 ai,...,an−1,an ,其中 1≤i≤n 。因此原问题的解可由子结构的解得到,具有最优子结构性质。
时间复杂度:已知序列 a1,..,aj 的最大子段和序列A(m,n),并记录形如 a1,...,aj 中的最大子段和序列A(i,j),求解序列 a1,...,aj+1 时,只需求解 max{sum(A(m,n)),sum(A(i,j)+A[j+1]),A[j]} ,即可得到新问题的解。设原问题的规模为n,则该算法的时间复杂度为 Θ(n) ,具有线性复杂度。
代码实现
/*********Dynamic programming algorithm*********/
subarray dp(vector<int> data, int low, int high)
{
subarray pre{low, low, data[low]};
subarray saRight = pre;
for (int i = low + 1; i < high + 1; i++)
{
if (data[i] >= (saRight.sum + data[i]))
{
saRight.sum = data[i];
saRight.high = i;
saRight.low = i;
}
else
{
saRight.sum += data[i];
saRight.high = i;
}
if (saRight.sum >= pre.sum)
{
pre = saRight;
}
}
return pre;
}