软件工程第三次作业

本文深入探讨了最大子段和问题,通过暴力法、前缀和优化及动态规划三种算法进行对比,详细讲解了如何在给定整数序列中找到最大子段和,以及动态规划算法的高效实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最大子段和问题


问题: 给定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。
-- 引用自《百度百科》

1. 分析

  • 暴力法
    我们很容易想到,枚举子区间的左端点和右端点LR,然后从L遍历到R求和。假设我们要处理的序列长度为N,显然,这种算法的时间复杂度是O(N^3),空间复杂度为O(1)。如果数据规模较大,这种算法无法满足需求。
  • 前缀和优化
    根据前一种方法,我们可以对求和部分进行优化,预处理出前缀和,可以在O(1)的时间复杂度完成求和操作,但需要额外开辟一段存储前缀和的空间。优化过后的算法时间复杂度是O(N^2),空间复杂度为O(N)。对于小规模的数据来说也许足够,但我们可以采取下边这种更加优秀的算法。
  • 动态规划
    定义dp[i]为右端点为i的最大子段和,于是我们很容易得出一个递推式dp[i] = max(dp[i - 1], 0) + arr[i]。然后,我们取ans = max{dp[i]}即为所求。最后,我们发现递推过程中其实只需要维护前一个dp值即可,于是可以把空间复杂度降至O(1)。这种算法的时间复杂度是O(N),空间复杂度为O(1),是已知的针对最大字段和的效率最高的算法。

2. 流程图

1645397-20190421170128013-478133713.png

3. 源代码

Coding传送门

int Solve_3(vector<int> & arr)
{
    int dp = 0;
    int _max = 0;
    for (int i = 0; i < arr.size(); ++i) {
        dp = max(dp, 0) + arr.at(i);
        _max = max(_max, dp);
    }
    return _max;
}

运行结果如下:
1645397-20190421205848399-1826422798.png

4.单元测试

条件组合覆盖

条件① : dp[i - 1] > 0 | dp[i - 1] <= 0
条件② : _max < dp[i] | _max >= dp[i]
条件组合① : dp[i - 1] > 0 && _max < dp[i]
条件组合② : dp[i - 1] > 0 && _max >= dp[i]
条件组合③ : dp[i - 1] <= 0 && _max < dp[i]
条件组合④ : dp[i - 1] <= 0 && _max >= dp[i]

当(a1,a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-20,-2) 时,一组测试足以覆盖以上四个条件组合。
测试代码如下:

#include "pch.h"
#include "CppUnitTest.h"
#include "../Max/mq.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTest
{
    TEST_CLASS(UnitTest)
    {
    public:
        
        TEST_METHOD(TestMethod1)
        {
            vector<int> arr = { -2,11,-4,13,-20,-2 };
            Assert::AreEqual(Solve_3(arr), 20);
        }
    };
}

测试结果如下:
1645397-20190421183601983-2145664225.png

author@miaoqi
2019 年 04 月 21日

转载于:https://www.cnblogs.com/miaoqi2019/p/10745183.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值