7、给定n个序列,求相邻的k个数之和为最大

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

这是一道典型的动态规划问题,可以使用C++来解决。我们可以将其命名为“最小化最大差距”(Minimize Maximum Difference)。以下是解决这个问题的一个步骤: 1. **状态定义**:设dp[i][j]表示前i个元素中,最多修改j个元素后,剩余部分最大差距的最小值。 2. **状态转移方程**: - 当j = 0 时(未修改任何元素),dp[i][j] = |ai - ai+1| (如果 i > 0,否则就是0,因为没有后续元素来影响差距) - 对于j > 0的情况,我们需要遍历所有可能的修改位置m (0 <= m < i),然后取两个子情况的最小值: - 不修改第m+1个元素,即 dp[m][j-1] - 修改第m+1个元素使其尽可能接近其他元素,这可以通过寻找前后元素中较小的那个来实现,更新为 min(dp[m][j-1], abs(ai - other)),where other 是ai的邻居。 3. **边界条件**: - 初始化dp[0][0] = 0(空序列的差距是0) - dp[i][k] = INT_MAX (极大值) 当 i == 0 或者 k < 0,防止意外溢出 4. **结果计算**:最终的答案是 dp[n][k] ```cpp #include <vector> using namespace std; int minimizeDifference(vector<int>& nums, int k) { int n = nums.size(); vector<vector<int>> dp(n + 1, vector<int>(k + 1)); // 基础情况 dp[0][0] = 0; for (int i = 1; i <= n; ++i) { for (int j = 0; j <= k; ++j) { if (j > 0) { dp[i][j] = numeric_limits<int>::max(); for (int m = 0; m < i; ++m) { int diff = nums[i - 1] - (m > 0 ? nums[m - 1] : nums[0]); dp[i][j] = min(dp[i][j], min(dp[m][j - 1], abs(diff))); } } else { dp[i][j] = abs(nums[i - 1] - (i > 0 ? nums[i - 2] : nums[0])); } } } return dp[n][k]; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值