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為問題規模。
參考