Problem 1003

       题源

       给一序列,求和最大的子序列,如:

         2
      5 6 -1 5 4 -7

      Case 1:
      14 1 4
     

      7 0 6 -1 1 -6 7 -5
      Case 2:
      7 1 6

2为序列数目。序列的一个数(5和7)是序列的元素个数。

很正常的想法,是定义一个数组接受数据,后对这个序列的每个子序列求和,找出最大的。当然其中还要运用一些技巧提高效率。下面是我写的程序:

#include <iostream>

using namespace std;

int main()
{
    int pre, start, max, end;
    int numOfInput, N;
    int* input; 

    cin >> numOfInput;
    for(int i = 0; i< numOfInput; i++)
    {
        cin >> N;
        input = new int[N];
        for(int j = 0; j< N;j++)cin >> input[j];

        max = 0;
        start = end = 1;
        for(int n = 0; n< N; n++)
        {
            pre = input[n];
            for(int m = n + 1; m< N; m++)
            {
                if(max < pre + input[m])
                {
                    pre += input[m];
                    max = pre;
                    start = n + 1;
                    end = m + 1;
                }
                else pre += input[m];
            }
        }
        cout << "Case " << i + 1 << ":\n";
        cout << max << " " << start << " " << end;
        if(i != numOfInput - 1)cout << endl << endl;
        else cout << endl;
    }
}

必然地:Time Limit Exceeded


程序最费时间的,是进行了n * (n – 1)/2比较。下面是网上流传的简单算法:

#include <iostream>
using namespace std;
int main()
{
    int T,N,num,startP,endP;
    cin >> T;
    for(int k = 0; k< T; k++)
    {
        cin >> N;
        int max = -1001,sum = 0,temp = 1;
        for(int i = 0; i< N; i++)
        {
            cin >> num;
            sum += num;
            if(sum> max)
            {
                max = sum;
                startP = temp;
                endP = i+1;
            }
            if(sum< 0)
            {
                sum = 0;
                temp = i+2;
            }
        }
        cout<< "Case " << k+1 << ":"<< endl << max << " " << startP << " " << endP << endl;
        if(k!=T-1) cout << endl;
    }
    return 0;
}

此算法是边输入边处理。理解此算法,先看一种输入序列特殊情况:最大子序列的起点是1,那么算法中二层循环的条件判断(sum> max)始终成立,后面的处理与我类似:sum为刚输入数据之前的和,如果sum与刚输入的数据的和大于max,那么最大值就需要变大了,否则max不变,sum更新。

   对于一般情况,这个算法比我更看清楚本质:

什么情况下satrtP不等于1呢?不等于1,说明新的起点之前的所有数据之和是一个负数,因为如果把它们纳进新的子序列只会把max拉低。比如下面的序列:

   a1,a2,a3,a4,a5,a6

如果最大子序列起点和终点是4,6,这说明a1 + a2 +a3< 0,否则a1加到a6是会把max的值提升的。

高效算法即是看清此点,当发现a1 + a2 +a3< 0,startP = 4,并以4作为新的“1”,前面的a1,a2,a3即变成无用的数据。也就是说,任何序列都可以转换成最大子序列起点为1的情况进行处理



点赞