HDOJ(1003) Max Sum

写的第一个版本,使用穷举(暴力)的方法,时间复杂度是O(N^2),执行时间超过限制,代码如下:

 1 #include <stdio.h>
 2 #define  MAX_LEN  100000UL
 3 
 4 int max_subsequence(int *array, unsigned int len, unsigned int *start, unsigned int *end)
 5 {
 6     int max_val = array[0], sum;
 7     int t_start = 0;
 8 
 9     *start = *end = 0;
10 
11     while(t_start < len){
12         sum = 0;
13         for (int i = t_start; i < len; i++){
14             sum += array[i];
15             if (sum > max_val){
16                 max_val = sum;
17                 *start = t_start;
18                 *end   = i;
19             }
20         }
21         ++t_start;
22     }
23     
24     *start += 1;
25     *end   += 1;
26     return max_val;
27 }
28 
29 int main(void)
30 {
31     int A[MAX_LEN];
32     int i = 0, round, n, max;
33     unsigned int start, end;
34 
35     scanf("%d", &round);
36 
37     while (i++ < round)
38     {
39         scanf("%d", &n);
40         for (int j = 0; j < n; j++){
41             scanf("%d", &A[j]);
42         }
43         max = max_subsequence(A, n, &start, &end);
44 
45         printf("Case %d:
", i);
46         printf("%d %d %d

", max, start, end);
47     }
48 
49     return 0;
50 }

接着,我又参考了一篇最大子串的文章,编写了下面的代码,成功AC,代码如下:

 1 #include <stdio.h>
 2 #define  MAX_LEN  100000UL
 3 
 4 int max_subsq(int *array, unsigned int size, unsigned int *start, unsigned int *end)
 5 {
 6     int max_sum = -2000, sum = 0;
 7     int curstart = *start = 0;
 8     unsigned int i;
 9 
10     for (i = 0; i < size; i++){
11         if (sum < 0){
12             sum = array[i];//丢弃之前的子串
13             curstart = i;  //将当前位置作为新的起始位置
14         } else {
15             sum += array[i];
16         }
17 
18         if (sum > max_sum){
19             max_sum = sum;
20             *start = curstart;
21             *end   = i;
22         }
23     }
24 
25     return max_sum;
26 }
27 
28 int main(void)
29 {
30     int A[MAX_LEN];
31     int i = 0, round, n, max;
32     unsigned int start, end;
33 
34     scanf("%d", &round);
35 
36     while (i++ < round)
37     {
38         scanf("%d", &n);
39         for (int j = 0; j < n; j++){
40             scanf("%d", &A[j]);
41         }
42         max = max_subsq(A, n, &start, &end);
43 
44         printf("Case %d:
", i);
45         printf("%d %d %d
", max, start + 1, end + 1);
46 
47         if (i != round) printf("
");
48     }
49 
50     return 0;
51 }

程序的基本思想为:记录前面子序列的和sum,如果和小于0,就丢弃这一段。为什么可以sum<0,就舍弃,重新开始扫描呢?以下证明

      我们用i表示子序列的起始下标,j 表示子序列的终止下标。原理是,当我们得到一个子序列,如果子序列的第一个数是非正数,那么可以舍去

      当一个子序列的前n个元素和为非正数时,是否也可以舍去呢?答案是可以的。假设k 是i到j中任意一个下标。Sum( a, b ) 表示子序列第a个元素到第b个元素之和。由于加到第j个元素,子序列才开始为负数,所以Sum( i, k ) > 0,Sum( i, k ) + Sum( k, j ) = Sum( i, j ) ,所以Sum( k, j ) < Sum( i, j ) < 0

      所以如果把 k到j的序列附加到j之后的序列上,只会使序列越来越小。所以i到j的序列都可以舍去。

但是,要是所有元素都是负数时,此程序能正确定位最大子序列吗?先构造下面的数据:

然后运行程序,结果如下:

因为每次都是先判断sum是否小于0,然后再添加新的元素,最后将sum与max_sum比较。如果全部为负数就变成了每次保留下来的子序列都只有一个元素(array[i]),这样,就相当于在所有负数中寻找最大值。可见,此算法是可行的。

原文地址:https://www.cnblogs.com/xiaomanon/p/4460480.html