ACM 子串和
子串和
时间限制:5000 ms | 内存限制:65535 KB
难度:3
- 描述
- 给定一整型数列{a1,a2...,an},找出连续非空子串{ax,ax+1,...,ay},使得该子序列的和最大,其中,1<=x<=y<=n。
- 输入
- 第一行是一个整数N(N<=10)表示测试数据的组数)
每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的一行里有n个整数I(-100=<I<=100),表示数列中的所有元素。(0<n<=1000000) - 输出
- 对于每组测试数据输出和最大的连续子串的和。
- 样例输入
-
1 5 1 2 -1 3 -2
- 样例输出
-
5
因为提前知道了要用动态规划思想,所以大大减少了难度。尽管如此,我还是用了两个小时才思考出答案。最后思路是正确的,但是最佳答案是边输入边判断。这样直接就减去了数组存储的过程。
动态规划,关键就是存储小问题的答案,避免重复计算小问题。大问题划分为小问题,这是分治的思想。
这个题的话存在这样一个公式b[n] = max(a[n] + b[n-1], a[n]) when n > 0 b[0] = a[0] when n = 0
子串和的最大值 = 数组b[n]的最大值#include <stdio.h> #include <stdlib.h> #define max(a,b) (a>b)?a:b int main() { int N; scanf("%d", &N); while (N--) { int i = 0, n = 0, m = 0, a, b; scanf("%d", &n); scanf("%d", &a); m = b = a; for(i = 1; i < n; i++) { scanf("%d", &a); b = max(a + b, a); m = max(b,m); } printf("%d\n", m); } return 0; }
在阅读《数据结构与算法 C语言描述》后,发现这个题还有好多解法,撇开时间复杂度是O(n^3)和O(n^2)的算法不谈。
书中也谈到了一个分治算法,没有使用动态规划,时间复杂度是O(NlogN),符合标准的分治递归算法的时间复杂度,原理写成公式一眼明了
f(串)= Max {f(左子串),f(右子串),f(左右串)} f(左右串)是从中间元素向左右两边扩展的所有子串的最大和
有了上面的规律后不难写出程序,下面的程序没有经过测试,不过原理是正确的
static int maxSubSum(const int a[], int left, int right) { if(left == right) { if(a[left] > 0) return a[left]; else return 0; } int maxLeft = 0, maxRight = 0, maxLeftBorder = 0, maxRightBorder = 0; int center = (left + right) / 2; maxLeft = maxSubSum(a, left, center); maxRight = maxSubSum(a, center+1, right); for(int i = center; i >= left; --i) { int maxLeftBorderTemp = maxLeftBorder + a[i]; if(maxLeftBorderTemp > maxLeftBorder) maxLeftBorder = maxLeftBorderTemp; } for(int i = center+1; i <= right; ++i) { int maxRightBorderTemp = maxRightBorder + a[i]; if(maxRightBorderTemp > maxRightBorder) maxRightBorder = maxRightBorderTemp; } int maxBorder = maxLeftBorder + maxRightBorder; return maxLeft > maxRight ? (maxLeft > maxBorder?maxLeft:maxBorder) : (maxRight>maxBorder?maxRight:maxBorder); }
还有一个时间复杂度同为O(n)的算法,非常经典,这种算法我自己是肯定想不出来的
书中的号称是最优的算法,叫做联机算法,仅需要常量空间并以线性时间运行。
经过研究这也是利用了动态规划思想。
b[n] = max(a[n] + b[n-1], a[n]) when n > 0 b[0] = a[0] when n = 0
同样是这个公式,我们可以改成这种样式
b[n] = max(b[n-1], 0) + a[n] when n > 0 b[0] = a[0] when n = 0
利用这个公式,就可以得出下面的程序
int maxSubSub(const int a[], int N) { int b, m, j; b = m = 0; for(j = 0; j < N; j++) { b += a[j]; if(b > m) m = b; else if(b < 0) b = 0; } return m; }