1. 程式人生 > >演算法筆記-1-最大子列和-Maximum Subsequence Sum

演算法筆記-1-最大子列和-Maximum Subsequence Sum

題目內容:

Given a sequence of KK integers {N1,N2,...,NK}. A continuous subsequence is defined to be {Ni,Ni+1,...,Nj} where 1ijK1ijK. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.

Input Specification:

Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K(10000). The second line contains K

numbers, separated by a space.

Output Specification:

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i

and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.

Sample Input:

10
-10 1 2 3 4 -5 -23 3 7 -21

Sample Output:

10 1 4

思路分析:

題目要求大致就是求出給出數列的最大子列和,同時給出所求子列的第一項和最後一項。對於全為負數的數列,最大子列和為0,並且輸出整個數列的第一項和最後一項。

從下面的程式碼中可以很容易看出這個演算法的對策就是,從頭開始累加,累加結果一旦小於0,就重置初始位置,並將和清零。

這個演算法的核心思想:

  1. 因為每當和小於0,sum就會被重置。
  2. 現在以這個演算法中每次重置sum的位置,做一個分割線,將這個陣列分為m個“段”。
  3. 對於每一個“段”,導致sum小於0的,一定是最後一個元素。也就是說,從段最左側開始,向右增加過程中,除最後一個子列外的任何一個子列和都不會小於0。那麼我們可以說任何一個這樣的子列對於數列和的增大是“有益的”。對於他後面的相鄰子列,如果出現一個最大值,那麼加上一個這個子列,一定會更大。所以,這樣的子列對於後面的相鄰子列來說,是“可以留下的”。那麼只要有“和不小於0”這個屬性的子列我們都可以留下。
  4. 由於我們所要檢查的數列還必須有一個屬性“連續”,前面說過,這個導致“段和”小於0的元素一定是最後一個。對於後面可能出現的最大子列來說,這個元素不能被拋棄,而如果不能拋棄他,前面的子列就算全加在一起,也是負的,對子列和“有害的”,所以必須拋棄它,拋棄的表現就是sum重置。
  5. 根據我們前面分析的,每一個“段”中都會出現一個最大子列和,而所有的“段”之間是完全分割的,那麼我們只要遍歷所有的“段”,選出那個最大的和就可以了。因為這個操作不受後出現子列的影響,所以可以嵌入大迴圈中。

程式碼1將4個if語句改寫成了利用短路求值的形式:

  1. 對於&&語句,條件為真時向後計算,否則退出
  2. 對於 | | 語句,條件為假時向後計算,否則退出

這裡是為了練習邏輯思考,實際編碼中最好寫成程式碼2的形式。
有問題歡迎評論區討論~

程式碼1:

#include <stdio.h>

int main()
{
    int n, flag = 1, first_flag = 1, positive_flag = 0;
    int sum = 0, summax = 0;
    int st = 0, end = 0, st_max = 0, end_max = 0, first = 0;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        scanf("%d", &end);

        first_flag && (first = end) && (first_flag = 0); // 短路求值
        end >= 0 && (positive_flag = 1);                 // 短路求值
        !flag || (flag = 0) || (st = end);               // 短路求值

        sum += end;
        if (summax < sum) {
            summax = sum;
            st_max = st;
            end_max = end;
        }
        !(sum < 0) || !(flag = 1) || (sum = 0);          // 短路求值
    }

    if (positive_flag)
        printf("%d %d %d", summax, st_max, end_max);
    else
        printf("%d %d %d", summax, first, end);
    return 0;
}

程式碼2:

#include <stdio.h>

int main()
{
    int n, flag = 1, first_flag = 1, positive_flag = 0;
    int sum = 0, summax = 0;
    int st = 0, end = 0, st_max = 0, end_max = 0, first = 0, last = 0;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        scanf("%d", &last);
        if (last >= 0) {
            positive_flag = 1;   // 檢查是否有正數,有正數則正數標記置1
        }
        if (first_flag) {
            first = last;        // 因為沒用陣列記錄所有資料,這裡就要用一個標記判斷並記錄第一個資料
            first_flag = 0;
        }
        if (flag) {
            st = last;           // 當sum < 0的時候要將下一項設為新的子列的開始項,
            flag = 0;            // 但是因為那時還沒讀取,所以就用一個標記記錄這件要做的事,
        }                        // 在下一個迴圈完成。

        sum += last;
        end = last;
        if (summax < sum) {      // 當前子列和小於最大和時,更新最大和,最大子列首項和末項
            summax = sum;
            st_max = st;
            end_max = end;
        }
        if (sum < 0) {           // 同上面flag項解釋,這裡flag置1,sum清零
            sum = 0;
            flag = 1;
        }
    }
    if (positive_flag)
        printf("%d %d %d", summax, st_max, end_max);
    else
        printf("%d %d %d", summax, first, last);
    return 0;
}