1.计划
1.1题目要求
问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。 -- 引用自 《百度百科》
2.开发
2.1分析
程序要求先输入序列的数据个数n
,之后输入序列的内容。如果n
不为正整数,则返回错误信息,如果n
为正整数,则继续输入序列内容。
要求最大子段和,可以使用分治法,将所给的序列Arr[n]分为等长的两个子段Arr[0] ~ Arr[n/2]和Arr[(n/2)+1] ~ Arr[n],对于这两个子段来说,最大子段和的出现情况有以下三种:
1.最大子段和在Arr[0]~Arr[n/2]中
2.最大子段和在Arr[(n/2)+1]~Arr[n]中
3.最大子段和在两个子段之间,即Arr[i]+...Arr[j] (1<=i<=n/2,n/2+1<=j<=n)
对于前两种情况,利用递归思想,可以把它们看作规模减半的原问题,利用递归方法可以求出最大子段和;对于第三种情况,我们需要在左半子段中找出S1=Arr[i]+...+Arr[n/2] (1<=i<=n/2),
在右半子段中找出S2=Arr[(n/2)+1]+...+Arr[j] (n/2+1<=j<=n)
,则最大子段和为S1+S2
,此时需要对原序列进行一次遍历。
递归函数的输入数据为数组地址,序列的左边界与右边界。递归的边界条件为左边界等于右边界,当达到递归的边界条件时,判断序列数据的正负,负数返回0,正数返回原值。
如果还没有达到递归的边界条件,则对子段进行遍历,求出左子段与右子段的最大值,并进行求和,赋给变量Sum
,将Sum
与左子段的和与右子段的和比较,取三者中最大的一个作为函数的返回值。
该算法为典型的分治算法,将每个问题分解为规模减半的子问题,再加上一次遍历算法,其时间复杂度为T(n)=O(n*logn) 。
算法的流程图如下:
2.2具体代码
Coding地址:点我
#include "stdlib.h"
#include "iostream"
#define Max 100
using namespace std;
int Array[Max];
int Maxnum( const int Arr[], int Left, int Right)
{
int Left_max = 0;
int Right_max = 0;
int Middle;
int Sum;
int Temp_sum;
int Left_sum;
int Right_sum;
Middle = (Left + Right) / 2;
if (Left == Right)
{
return Arr[Left] > 0 ? Arr[Left] : 0;
}
else
{
Left_sum = Maxnum(Arr, Left, Middle);
Right_sum = Maxnum(Arr, Middle + 1, Right);
Temp_sum = 0;
for (int i = Middle; i >= Left; i--)
{
Temp_sum += Arr[i];
if (Temp_sum > Left_max)
Left_max = Temp_sum;
}
Temp_sum = 0;
for (int i = Middle + 1; i <= Right; i++)
{
Temp_sum += Arr[i];
if (Temp_sum > Right_max)
Right_max = Temp_sum;
}
Sum = Left_max + Right_max;
if (Left_sum > Sum)
{
Sum = Left_sum;
}
if (Right_sum > Sum)
{
Sum = Right_sum;
}
}
return Sum;
}
int main()
{
int i = 0;
int n = 0;
cin >> n;
while (n <= 0)
{
cout << "Error,illegal number!try again" << endl;
cin >> n;
}
for (i=0; i < n; i++)
{
cin >> Array[i];
}
cout << Maxnum(Array, 0, n - 1) << endl;
system("pause");
return 0;
}
2.3测试
测试用例的覆盖标准为判定/条件覆盖,即判断中每个条件的所有可能取值至少执行一次,同时每个判断本身所有可能结果也至少执行一次。
要实现判定/条件覆盖,需要设计的测试用例应该满足以下条件:
- 设计序列,使其最大子段位于左半子段中的测试用例;
- 设计序列,使其最大子段位于右半子段中的测试用例;
- 设计序列,使其最大子段位于两子段之间的测试用例;
- 设计的测试用例中应含有负数;
- 设计序列全为负数的测试用例;
- 设计序列全为0的测试用例;
2.3.1测试代码如下:
#include "stdafx.h"
#include "CppUnitTest.h"
#include "..\Project_3\Project_3.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTest
{
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(TestSample)
{
int Arr[] = { -2,11,-4,13,-5,-2 };
Assert::AreEqual(Maxnum(Arr, 0, 5), 20);
}
TEST_METHOD(TestMethod_Left)
{
int Arr[] = { -1,1,2,3,4,-2,-3,-5,1,2 };
Assert::AreEqual(Maxnum(Arr, 0, 9), 10);
}
TEST_METHOD(TestMethod_Right)
{
int Arr[] = { -2,-4,-6,2,1,-5,8,8,-1,2 };
Assert::AreEqual(Maxnum(Arr, 0, 9), 17);
}
TEST_METHOD(TestMethod_Middle)
{
int Arr[] = { -2,-4,-6,2,3,-1,5,7,-2,3 };
Assert::AreEqual(Maxnum(Arr, 0, 9), 17);
}
TEST_METHOD(TestMethod_AllMinus)
{
int Arr[] = { -2,-4,-6,-1,-7,-1,-5,-7,-2,-3 };
Assert::AreEqual(Maxnum(Arr, 0, 9), 0);
}
TEST_METHOD(TestMethod_AllZero)
{
int Arr[] = { 0,0,0,0,0,0,0,0,0,0};
Assert::AreEqual(Maxnum(Arr, 0, 9), 0);
}
};
}
2.3.2测试结果的截图如下:
2.3.3测试结果摘要
组名称: UnitTest
分组依据: Hierarchy
组全名: UnitTest
持续时间: 0:00:00.0005239
0 个测试失败
0 个测试跳过
6 个测试通过
结果1 名称: TestMethod_AllMinus
结果1 结果: 已通过
结果1 持续时间: 0:00:00.0000427
结果1 StackTrace:
结果1 消息:
结果1 StandardOutput:
结果1 StandardError:
结果2 名称: TestMethod_AllZero
结果2 结果: 已通过
结果2 持续时间: 0:00:00.000036
结果2 StackTrace:
结果2 消息:
结果2 StandardOutput:
结果2 StandardError:
结果3 名称: TestMethod_Left
结果3 结果: 已通过
结果3 持续时间: 0:00:00.0000513
结果3 StackTrace:
结果3 消息:
结果3 StandardOutput:
结果3 StandardError:
结果4 名称: TestMethod_Middle
结果4 结果: 已通过
结果4 持续时间: 0:00:00.0000361
结果4 StackTrace:
结果4 消息:
结果4 StandardOutput:
结果4 StandardError:
结果5 名称: TestMethod_Right
结果5 结果: 已通过
结果5 持续时间: 0:00:00.0000368
结果5 StackTrace:
结果5 消息:
结果5 StandardOutput:
结果5 StandardError:
结果6 名称: TestSample
结果6 结果: 已通过
结果6 持续时间: 0:00:00.000321
结果6 StackTrace:
结果6 消息:
结果6 StandardOutput:
结果6 StandardError:
总结
这次的个人实战项目,我选择的是求最大子段和,使用的算法是分治法,因为当初计划的时候并没有考虑到时间复杂度更简单的动态规划方法,所以设计的过程显得些许复杂,但是算法的实现原理很容易理解,也算是一个优点吧。
设计的过程,我是按照PSP(个人开发流程)来进行实践的,刚开始对于这种流程确实不是很习惯,但这套流程对于我提升自己的实力有很大的帮助,而且今后进行个人软件工程的开发,都需要遵守PSP模型。
虽说是依照PSP来开发的,但是是有很多细节上的问题没有解决,比如PSP模型中的很多步骤我都省略了,没有严格按照流程开发,这是今后需要改进的问题。还有我对于时间的规划并不合理,这也是需要改进的地方。