最大子数组和问题

来源:互联网 发布:数据漫游三星在哪 编辑:程序博客网 时间:2024/04/30 06:46

动态规划(dynamic programing)是通过组合子问题的解而解决整个问题的。这里Programming 翻译写成规划而不是编程。维基百科上写到 This is  also usually done in a tublar form by iteratively genereting solutions to bigger and bigger subprobleams by using the solutions to small subprobleams. 说明动态规划的关键是子问题,而且动态规划的子问题是相互关联的。而分治算法是将问题分成相互独立的子问题,递归解决所有的子问题。 然后合并子问题会做很多不必要的工作,即重复的求解公共子问题。

动态规划对每个子问题都求解一遍,并且将其结果保存在表中,从而避免了子问题被重复计算。 

适用范围

1. 动态规划问题通常情况下应用于最优化问题,这类问题一般都有多个可行的解,每个解有一个值,而我们希望从中找到最优的答案。

2. 该问题必须符合无后效性。即当前状态是历史的完全总结,某阶段的状态一旦确定,则此后过程的演变不再受此前各种状态及决策的影响,简单的说,就是“未来与过去无关”,当前的状态是此前历史的一个完整总结,此前的历史只能通过当前的状态去影响过程未来的演变。具体地说,如果一个问题被划分各个阶段之后,阶段I中的状态只能由阶段I-1中的状态通过状态转移方程得来,与其它状态没有关系,特别是与未发生的状态没有关系。

简单动态规划问题

题目  最大子数组和问题

一个有N个整数元素的一个数组(A[0],A[1],.........A[N]),这个数组当然有很多子数组,那么数组之和的最大值是什么呢??   这是一道很简单的题目,暴力搜索的时间复杂度为O(N^2)。

例有数组 int A[5] = {-1, 2, 3, -4, 2};

符合条件的子数组为  2,3  即答案为 5;
再明确一下题意
1.子数组必须是连续的。
2.返回子数组的具体位置。
3.数组中包含:正整数,零,负整数。
例如
数组: {1, -2, 3, 5, -3, 2}   返回值为 8
数组: {0, -2, 3, 5, -1, 2}   返回值为 9
数组: {-9, -2, -3, -5, -6}   返回值为 -2  注意子数组不能为空
首先我们看看最直接的穷举法

int MaxSubString(int* A, int n)
{
  int max = min;  //初始值为负无穷大
  int sum;
  for(int i = 0; i < n; i++)
  {
    sum = 0;
    for(int j = i; j < n; j++)
    {
      sum += A[j];
      if(sum > max)
        max = sum;
    }
  }
  return max;
}

这种方法最直接,当也是最耗时的,他的时间复杂度为O(n^2);
问题分析
可以优化吗?答案是肯定的,可以考虑数组的第一个元素,以及最大的一段数组(A[i], ..., A[j]),和A[0]的关系,有一下几种情况:
1. 当0 = i = j 时,元素A[0]本身构成和最大的一段
2. 当0 = i < j 时,和最大的一段以A[0]开始;
3. 当0 < i 时, 元素A[0]和最大的一段没有关系。

从上面3中情况可以看出。可以将一个大问题(N个元素数组)转化为一个较小的问题(N-1个元素的数组)。假设已经知道(A[1], ...,A[n-1])中和最大的一段数组之和为All[1],并且已经知道
(A[1],...,A[n-1])中包含A[1]的和最大的一段数组为Start[1]。那么不难看出 (A[0], ..., A[n])中问题的解All[0] = max{ A[0], A[0] + start[1], All[1] }。通过这样的分析,可以看出这个问题
无有效性,可以用动态规划来解决。
解决方案

#include <iostream>
using namespace std; 

int max(int a, int b) {
if (a >= b) return a;
else  return b;
}

int  MaxAdd(int *A, int n){
int end = A[1];
int All = A[1];
for (int i = 2; i <= n; i++)
{  
end = max(A[i], end + A[i]);
All = max(All, end);
}
return  All; 
}

int main() {
int n, A[2000]; 
cin >> n; 
for (int i = 1; i <= n; i++)
{
cin >> A[i]; 
}
   cout<<MaxAdd(A, n)<<endl; 
}
我们通过动规算法解决该问题不仅效率很高(时间复杂度为O(n)),而且极其简便。

0 0