最大m子段和动态规划_最大子段和——动态规划是如何运作的

什么是动态规划呢? 以我个人的理解来说,动态规划,顾名思义,就是规划一个方案时,方案是随着条件的改变而发生动态改变的。要使用动态规划解决一个问题,首先我们需要找到最优子结构,其次我们需要找出子问题的重叠性质。最后利用重叠性质循环寻找最优子结构来达到规划出最优解的方案,下面我们举一个例子看看。

最大子段和:

给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。

输入格式:

输入有两行:

第一行是n值(1<=n<=10000);

第二行是n个整数。

输出格式:

输出最大子段和。

输入样例:

在这里给出一组输入。例如:

6
-2 11 -4 13 -5 -2

输出样例:

在这里给出相应的输出。例如:

20

如何使用动态规划寻找最大子段和呢?我们首先需要找到最优子结构。

设序列a[1]、a[2]、···、a[n]为输入序列。则最大子段和可以表示为:

193acc43a34e01a10f7b1d405b14d543.png

我们另设序列b[1]、···、b[n],其中b[j]表示从a[1]至a[j]的最大子段和。那么

b3030aa53a831e18d049526d11283af0.png

考虑b[j]的性质,可以得出结论:

9e97b9b7e08a031d7df13cf64b9150c2.png

由b[j]得到的结论,我们便得到了最优子结构。而重叠性质能非常显而易见的得到: 作为最优子结构中有且仅有的一个取值范围在[1,n]的数值j,从初始到结束循环一遍即可。

下面我给出示例代码:

#include

#define M INT_MAX

using namespace std;

int findMax(int *data,int lenth){

int bj = 0,finaldata = -M;

        for(int i = 0;i < lenth;i++){

if(bj > 0)

bj += data[i];

else bj = data[i];

    if(bj > finaldata) 

finaldata = bj;

}

return finaldata;

}

int main(){

int lenth,flag = 0;

cin>>lenth;

int *data = new int [lenth];

for(int i = 0;i < lenth;i++){

cin>>data[i];

if(data[i] > 0) flag = 1; 

}

if(flag) cout<

else cout<

}

其中下划线代码是重叠性质循环,斜体代码是最优子结构。该算法时间复杂度显然为O(n)。接下来的代码主要修改findMax函数,对主函数不进行大修改。由于最优子结构的非人性,在限时题目中,尤其是在考试中,我们可能不会能及时想到上面罗列的表达式。而我们在思考算法问题时,往往第一时间或者最先想到的思路是作为一个自然人,我们会如何解决此类问题,接下来我们抛开表达式思考。当我们仅凭自己的大脑去解决最大子段和问题时,条理最清晰(或者最笨)的方法是我们把所有的子段和全部罗列出来,然后比较出最优解。这种方法我们叫做穷举法:

int findMax(int *dataSum,int *data,int lenth){

for(int i = 1;i < lenth;i++){

dataSum[i] = dataSum[i - 1] + data[i]; 

}

int themax = -M;

for(int i = 0;i < lenth;i++)

  for(int j = i + 1;j < lenth;j++){

      themax = max(themax,dataSum[j]-dataSum[i]);

    }

return themax;

}

其中斜体代码就是穷举过程,显然改算法的时间复杂度为O(n^2)。但这个时间复杂度有点高,接下来我们思考如何一遍循环解决问题。我们需要思考仅循环一次时最大子段和该如何产生。如果我们把全部序列看作一个总系统, 最大子段和看作一个子系统,这个子系统显然是从 总系统的左侧(从a[1])开始产生,并不断向右扩展。在情况十分乐观(a[k]序列中没有负数)时,子系统会延伸成为 总系统。那么什么时候子系统无法成为总系统呢?答案是 原有子系统崩坏就无法成为总系统了。如何理解子系统崩坏,就是说这个系统 已经烂到保留它的价值已经为负数。举个例子: 1,3,-2,4,-10,5 在子系统为1,3,-2时,子系统的价值为2,还有保留的价值。而当子系统为1,3,-2,4,-10时,子系统的价值为-4,已经崩坏,此时保留子系统对于接下来的5来说没有任何积极意义——因为不如把5单独作为一个系统。 这样,我们就找到了选择方案。接下来我们挑出寻找过程中的子段和的最大值即可。那么选择方案的意义在哪里呢?其意义就在于让子系统及时剔除有害的原系统,防止在后续寻找时产生误判。下面给出代码:

int findMax(int *data,int lenth){

int prodata = 0,finaldata = -M;

for(int i = 0;i < lenth;i++){

        prodata = max(prodata + data[i],data[i]);

    if(prodata > finaldata) 

finaldata = prodata;

}

return finaldata;

}

其中下划线代码是选择剔除,斜体代码是筛选最大子段和。

比较第一段和这一段代码,我们会发现,其实这两段代码的差别不大,思想上也是极为相近的,可以理解为一个算法的不同理解。其实算法最重要的也就是思考角度。如果你有一个清晰的解题思路,那么其实动态规划是什么你也不必过多考虑。

如果你看到了这里,说明你仍在试图寻找自己感兴趣的东西,马上就给!

 ちびパイア ~吸血姉妹とエッチでビッチな

  • 食用建议:

1.食用地点:PC

2.食材提醒:拔作

3.风格:日常、loli、吸血鬼

  • 传输门:小吸血鬼

据说是本子改的游戏,懂得都懂,剧情就不多说了。男主要养两只小吸血鬼,真的累啊。画风一般,人物比例有些别扭。所以我将它作为近期的避险资源发布。

a3e9a9b274e50553ac963cb18e38a965.png

如果对文章内容有疑问请在 “如何食用” 栏中点击 “文章说明” 查看相关说明。或者直接点击这里。希望您能在这里收获很多快乐~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值