动态规划-最大子段和

来源:互联网 发布:数据质量责任追究制度 编辑:程序博客网 时间:2024/06/08 18:09

一、最大子段和

    问题: 给定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.最大子段和问题的暴力算法

    穷举所有的[1,n]之间的区间,对于待求序列X,求出X的每一个子段及其和,求出最大字段和;

/* 使用暴力法求解最大字段和 */int volient(int* arr, int n){int i, j, k;int maxsum = 0;for (i = 0; i < n; i++)for (j = i; j < n; j++){int tsum = 0;for (k = i; k <= j; k++)tsum += arr[k];if (tsum > maxsum)maxsum = tsum;}return maxsum;}

2.分治法求解最大字段和问题

    如果将所给的序列a[1:n]分解为长度相等的两段a[1:n/2]和a[n/2+1:n],分别求出这两段的最大子段和,则a[1:n]的最大子段和有三种情形:

    a.a[1:n]的最大子段和与a[1:n/2]相同;

    b.a[1:n]的最大子段和与a[1:n/2]相同;

    c.a[1:n]的最大子段和跨越中间节点,为A{ai,ai+1,ai+2...aj};且1<=i<=n/2;n/2+1<=j<=n;

算法实现:

int divide(int* arr, int left, int right){int i, mid, leftsum, rightsum;int lchildsum = 0, lchildmax = 0, rchildsum= 0, rchildmax = 0, sum = 0;if (left == right) return arr[left] >= 0 ? arr[left] : 0;mid = (left + right) / 2;leftsum = divide(arr, left, mid);rightsum = divide(arr, mid+1, right);for (i = mid; i >= left; i--){lchildsum += arr[i];if (lchildsum > lchildmax)lchildmax = lchildsum;}for (i = mid + 1; i <= right; i++){rchildsum += arr[i];if (rchildsum > rchildmax)rchildmax = rchildsum;}sum = lchildmax + rchildmax;if (leftsum > sum) sum = leftsum;if (rightsum > sum) sum = rightsum;return sum;}

3. 使用分治法求解最大子段和问题

    若记b[j]=max(a[i]+a[i+1]+..+a[j]),其中1<=i<=j,并且1<=j<=n。则所求的最大子段和为max b[j],1<=j<=n,我们令b[j]表示以位置 j 为终点的所有子区间中和最大的一个:

    a.如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中

    b.如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含

    c.如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大

算法实现:

int dynamic(int* arr, int n){int i, sum = 0;for (i = 0; i < n; i++){dp[i] = max(dp[i-1] + arr[i], arr[i]);if (dp[i] > sum)sum = dp[i];}return sum;}


   



0 0