编程之美上的一道题,今天在别的地方看别人用贪心写的,真心觉得不对,所以做了一下。
一个有N个元素的一维数组(a[0], a[1]....a[n-1]),我们定义连续的a[i] ~ a[j],0<= i, j <=n-1为子数组。
显然这个数组中包含很多子数组,请求最大的子数组之和。
如果不想时间复杂度,用遍历所有可能子数组,然后找出最大值就可以了。
现在如果要求时间复杂度最小,那么肯定是要DP解的。
我们假设定义两个数组:
all[i]:表示从i~n-1,最大的子数组之和。
start[i]:表示包含i,并且从i~n-1,最大子数组之和。
all[i]中max只有三种可能:
(1) a[i]单独就是最大,之后再加一个就会变小。
(2)a[i]+...a[j]最大,即start[i]最大
(3)a[x]+..a[j]最大,即不包含i的后序某一个子数组和最大。
最终,最大的子数组之和是all[0]。根据上述3个可能,很容易写出如下递推式:
start[i] = max (a[i], a[i]+start[i+1])
all[i] = max(start[i], all[i+1])
注意我们把上面max(a, b, c)拆成了两个max(a, b)
由于我们在计算start[i]/all[i]时候需要start[i+?]的值,所以我们从后向前递推dp。
代码如下,时间复杂度O(n):
#include <stdio.h> #include <stdlib.h> #include <string.h> int max(int a, int b) { if(a>b) { return a; }else { return b; } } int max_sum(int* arr, int n) { // Helper array int i; int* start = (int*)malloc(sizeof(int)*n); int* all = (int*)malloc(sizeof(int)*n); int final; if(!start || !all) { return -1; } memset(start, 0, sizeof(int)*n); memset(all, 0, sizeof(int)*n); // dp start[n-1] = arr[n-1]; all[n-1] = arr[n-1]; for(i=n-2;i>=0;i--) { start[i] = max(arr[i], arr[i]+start[i+1]); all[i] = max(start[i], all[i+1]); } final = all[0]; // Free helper array free(start); free(all); return final; } int main() { //int arr[6] = {1, -2, 3, 5, -3, 2}; // 8 int arr[6] = {0, -2, 3, 5, -1, 2}; // 9 //int arr[5] = {-9, -2, -3, -5, -3}; // -2 printf("max sum of sub_arr: %d \n", max_sum(arr, sizeof(arr)/sizeof(int))); return 0; }