1、问题描述:给定n个数,求相邻的k个数之和为最大。要求给出复杂度较小的一种算法
再解决这个问题前,先了解一下类似的常见问题。
2、给定一串数字(可正可负的int,放在数组Num里),要求找到起始位置start和终止位置end,使得从start位置到end位置的所有数字之和最大,返回这个最大值max。
算法思想:用动态规划算法实现。
设 iMaxSum 为以 iNumPar[i] 终止且包含 iNumPar[i] 的最大序列的和,有:
iMaxSum = iNumPar[0];
iMaxSum = iMaxSum > 0 ? iMaxSum + iNumPar[i+1] : iNumPar[i+1];
其中,i = 0,1,...
那么和iMaxSum 就是 iNumPar[0] ... iNumPar[n] 中最大的一个序列。
算法的时间复杂度为O(n)。
示例代码
#include <iostream> using namespace std; void FindTheMaxSum(int iNumPar[], const int iSize, int &ibegin, int &iend, int &iMaxSum) { ibegin = iend = 0; int iTemSum = iNumPar[0], iStart = 0; iMaxSum = iTemSum; for (int i = 1; i < iSize; ++i) { if (iTemSum > 0) { iTemSum = iTemSum + iNumPar[i]; } else { iTemSum = iNumPar[i]; iStart = i; } if (iTemSum > iMaxSum) //更新结果 { iMaxSum = iTemSum; ibegin = iStart; iend = i; } } } int main() { int ibegin = 0, iend = 0, iResult = 0, iCount = 0; int iNum[] = {-1, -2, -3, -4, 0, 1, 2, 3, 4, 5, -20, 40, -3}; iCount = sizeof(iNum)/sizeof(iNum[0]); FindTheMaxSum(iNum, iCount, ibegin, iend, iResult); cout << ibegin <<" "<< iend <<" "<< iResult <<endl; return 1; }
3、最容易想到的就是穷举搜索。在n个序列中求出每k个连续序列的值,进行比较。
示例代码
#include <iostream> using namespace std; void FindTheMaxKSum(int iNumPar[], const int iSize, int iK, int &ibegin, int &iend, int &iMaxSum) { //iK指出求最大各子序列的长度 int iTemMaxKsum = 0; for (int i = 0; i < iK; ++i) { iTemMaxKsum += iNumPar[i]; } iMaxSum = iTemMaxKsum; int iStart = ibegin = 0, iOver = iend = iK - 1; for (int i = 1; (i < iSize) && ((iSize - i) >= iK); ++i) { iTemMaxKsum = 0; for (int j = i; ((j - i) < iK) && (j < iSize); ++j) { iTemMaxKsum += iNumPar[j]; } if (iTemMaxKsum > iMaxSum) { iMaxSum = iTemMaxKsum; ibegin = i; iend = i + iK - 1; } } } int main() { int ibegin = 0, iend = 0, iResult = 0, iCount = 0; int iNum[] = {-1, -2, 4, 5, 6, 1, 2, 3, 4, -4, -20, 40, -3}; iCount = sizeof(iNum)/sizeof(iNum[0]); FindTheMaxKSum(iNum, iCount, 3, ibegin, iend, iResult); cout << ibegin <<" "<< iend <<" "<< iResult <<endl; return 1; }
由上可见,复杂度为O(iK + n*k)。
那么有没有更好的方法呢?优化的方法就是把循环体内的相加循环去掉。
示例代码
#include <iostream> using namespace std; void FindTheMaxKSum(int iNumPar[], const int iSize, int iK, int &ibegin, int &iend, int &iMaxSum) { //iK指出求最大各子序列的长度 int *pTempSum = new int[iSize]; pTempSum[0] = iNumPar[0]; for (int i = 1; i < iSize; i++) { pTempSum[i] = pTempSum[i - 1] + iNumPar[i]; } int iStart = ibegin = 0; ibegin = 0; iend = iK - 1; iMaxSum = pTempSum[iend]; for (int i = 1, j = iK; j < iSize; ++i, ++j) { if ((pTempSum[j] - pTempSum[i - 1]) > iMaxSum) { iMaxSum = pTempSum[j] - pTempSum[i - 1]; ibegin = i; iend = j; } } delete pTempSum[]; } int main() { int ibegin = 0, iend = 0, iResult = 0, iCount = 0; int iNum[] = {-1, -2, 4, 5, 6, 1, 2, 3, 4, -4, -20, 40, -3}; iCount = sizeof(iNum)/sizeof(iNum[0]); FindTheMaxKSum(iNum, iCount, 3, ibegin, iend, iResult); cout << ibegin <<" "<< iend <<" "<< iResult <<endl; return 1; }
由上可见,程序复杂度为O(n),n为问题规模。
参考
[1]更多的数学说明:
http://www.cnblogs.com/Jason_Yao/archive/2009/09/27/1574713.html
[2]CSDN论坛中的讨论
http://topic.csdn.net/u/20110402/12/795e568e-b590-4fb0-8a4d-dba8b408441f.html?1021658511