基本解题思路:动态规划,不考虑穷举,分治。
根据网上,状态转移方程是:MaxSum[i] = Max{ MaxSum[i-1] + A[i], A[i]}
翻译公式:到当前位置i 时,最大子序列和为: i-1 时最大子序列和 + 当前数 与 当前数 相比中大的那个。
(翻译的不清楚也就说明对问题了解不够)
这个状态转移公式即为解决问题,划分问题的方法。这样划分子问题对不对。。。。。不去证明。
从理解的角度上来说,对于一个数串,把它分为maxsum(a0…ai-1),ai。那么对于i,来说,最大子序列和的值即为,maxsum(ai-1)+ ai 或者 ai。
对于初始状态 i = 0; 时, maxsum = a0; 当 i=1 时 maxsum = maxsum(a0) + ai or ai。
废话这么多,也没证明什么出来,反正从理解公式上来说,这个状态转移公式提出了问题的解决办法。
实际操作上来说。有三个变量:sum maxsum i.
对于数组A, 从0 开始计算sum, sum = a0 + … + , maxsum 设置为-10010, i 从 0开始计算。
当sum > maxsum 时, maxsum = sum.
若sum < 0 , sum = 0 及说明目前的sum+ai, 肯定小于 ai, 即重新开始计算子串值。
将maxsum设为一个很大的负数的好处是就不用考虑数组a全为负数的情况。
对于最大值的子串的索引,可以用三个变量表示,start temp end,初始值全设为0,
如果sum > max start = temp, end = i
如果sum < 0 sum = 0 temp = i+1
由于结果比实际的索引大1 则需要对start end 加一。
参考代码
http://blog.csdn.net/worker90/article/details/6649897
下面是我写的代码。
#include <stdio.h> #include <stdlib.h> void max_sum(int *a, int solution[3], int N) { int sum, i, j, maxsum; sum = 0; j = 0; int start, temp, end; maxsum = -10001; temp = 0; start = 0; end = 0; for (i = 0; i < N; i++) { sum += a[i]; if (sum > maxsum) { maxsum = sum; start = temp; end = i ; } if (sum < 0) { sum = 0; temp = i + 1; } } solution[0] = maxsum; solution[1] = start +1; solution[2] = end + 1; } int main(void) { int T, N; int a[100002], solution[3]; int i; int j = 1; scanf("%d", &T); while (T > 0) { scanf("%d", &N); for (i = 0; i < N; i++) { a[i] = 0; scanf("%d", &a[i]); } max_sum(a, solution, N); printf("Case %d:\n", j); printf("%d %d %d\n", solution[0], solution[1], solution[2]); if (T > 1) { printf("\n"); } j++; T--; } system("pause"); }