HDU 1003 Max sum

原題: http://acm.hdu.edu.cn/showproblem.php?pid=1003

看到這題的時候一臉懵逼,網上尋找解答,發現都講的不詳細,變量聲明也很短,根本看不出是什麼用途,這裏給出了我的解答,參考了網上各位大神的方法,加上了註釋,方便學習。

0x00 分治法解題

#include <stdio.h>

struct Sequence
{
    //表示一個數組

    int sum;//數組元素之和
    int leftPosition;//數組左邊界位置
    int rightPosition;//數組右邊界位置
};

/*
    求最大子列:

    首先明確:
        給定一個數列
        其最大子列的範圍,要麼包括正中間的位置,要麼全在最中間位置的左邊,要麼全在最中間位置的右邊
        這三種情況在迭代函數中分別處理
*/

struct Sequence getMaxSequence(int * sourceSequence, int leftPosition, int rightPosition) {

    // printf("into: %d, %d\n",leftPosition,rightPosition);
    int mediumPosition;

    //範圍:[leftPosition, rightPosition]
    if (leftPosition == rightPosition) {
        //給定範圍內的子數列只有一個元素,構造一個struct sequence來描述;
        struct Sequence sequence;
        sequence.leftPosition = leftPosition;

        sequence.rightPosition = rightPosition;
        sequence.sum = sourceSequence[leftPosition];
        // printf("return: %d, %d\n",sequence.leftPosition,sequence.rightPosition);

        return sequence;
            //將這個元素返回
    }

    //取得中間位置
    mediumPosition = (leftPosition + rightPosition) / 2;

    //構造三種struct來保存三種情況的返回值;
    struct Sequence leftSequence;
    struct Sequence rightSequence;
    struct Sequence aroundSequence;

    leftSequence = getMaxSequence(sourceSequence, leftPosition, mediumPosition);

    rightSequence = getMaxSequence(sourceSequence, mediumPosition + 1, rightPosition);

    //第三種情況下,先從中間向左邊查找找出最大的和,再從中間向右邊查找找出最大的和,相加得到橫跨中間位置的最大值及範圍
    //先是左邊
    //先保存向左邊出發遇到的第一個值
    int leftMaxSumTmp = sourceSequence[mediumPosition];
    //並保存該值的位置
    aroundSequence.leftPosition = mediumPosition;

    int leftSumTmp = 0;
    for (int index = mediumPosition; index >= leftPosition; index--) {
        leftSumTmp = leftSumTmp + sourceSequence[index];
        if (leftSumTmp >= leftMaxSumTmp) {
            //如果發現從中間到左邊某一項的所有值之和比leftMaxSumTmp更大了,就更新leftMaxSumTmp
            //並記錄此位置到aroundSequence的leftPosition字段
            leftMaxSumTmp = leftSumTmp;
            aroundSequence.leftPosition = index;
        }

    }
    //再是右邊
    //先保存向右邊出發遇到的第一個值(因爲到這裏leftPosition和rightPosition不相等所以meiumPosition + 1(一定小於等於rightPosition)一定沒有超過範圍:[leftPosition, rightPosition]),不加if
    int rightMaxSumTmp = sourceSequence[mediumPosition + 1];
    //保存該值的位置
    aroundSequence.rightPosition = mediumPosition + 1;

    int rightSumTmp = 0;
    for (int index = mediumPosition + 1; index <= rightPosition; index++) {
        rightSumTmp = rightSumTmp + sourceSequence[index];
        if (rightSumTmp >= rightMaxSumTmp) {
            //如果發現從中間到右邊某一項的所有值之和比rightMaxSumTmp更大了,就更新rightMaxSumTmp
            //並記錄此位置到aroundSequence的rightPosition字段
            rightMaxSumTmp = rightSumTmp;
            aroundSequence.rightPosition = index;
        }

    }

    aroundSequence.sum = leftMaxSumTmp + rightMaxSumTmp;

    //三選一返回,選sum最長的那個返回

    // printf("return: %d, %d\n",finalSequence.leftPosition,finalSequence.rightPosition);
    return (leftSequence.sum > rightSequence.sum) ? ((leftSequence.sum > aroundSequence.sum) ? leftSequence : aroundSequence) : ((rightSequence.sum > aroundSequence.sum) ? rightSequence : aroundSequence);

}
int main() {
    int numOfCase , indexOfCase = 1,lengthOfSquence;
    scanf("%d", &numOfCase);
    while (indexOfCase <= numOfCase) {
        scanf("%d",&lengthOfSquence);
        int sourceSequence[100000];
        for(int index = 0;index <lengthOfSquence;index++){
            scanf("%d",sourceSequence + index); 
        }
        struct Sequence aimSequence =  getMaxSequence(sourceSequence,0,lengthOfSquence - 1);
        printf("Case %d:\n%d %d %d\n", indexOfCase ,aimSequence.sum,aimSequence.leftPosition + 1,aimSequence.rightPosition + 1);
        if(indexOfCase != numOfCase){
            printf("\n");
        }
        indexOfCase++;
    }

    return 0;
}

寒假看到這樣一篇文章,講的很詳細
http://conw.net/archives/9/#comment-25

0x01 動態規劃

#include <iostream>
#include <cstdio>
#include <cstring>

#define iinf 1e9

using namespace std;

// O(n)AC

// 動態規劃
// 原先維護一個dp數組,dp[i]保存以第i個位子結尾的所有子列的最大和
// dp[i] = list[i] + max(dp[i - 1], 0);
// 自處省略這個數組,用兩個變量來實現

int main(int argc, char const *argv[])
{

    int T, N, last, now, ans, startpos, l, r;
    scanf("%d", &T);
    for (int j = 1; j <= T; j++) {
        last = -1;
        ans = -iinf;
        l = 1;
        r = 1;
        scanf("%d", &N);

        for (int i = 1; i <= N; i++) {
            scanf("%d", &now);
            if (last >= 0) {
                last = now + last;

            } else {
                last = now;
                startpos = i;

            }

            if (last > ans) {
                ans = last;
                l = startpos;
                r = i;
            }

        }

        if (j == 1) {
            printf("Case 1:\n");
        } else {
            printf("\nCase %d:\n", j);
        }

        printf("%d %d %d\n", ans, l, r);

    }

    return 0;
}

0x02 結合優化後的前綴數組

用這種用法的人似乎更多

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define iinf 1e9

using namespace std;

// O(n)AC

// 另一種結合前綴數組的方法
// data[i] = list[0...(i - 1)];
// data數組從1開始避免對邊界的判斷
// 則list[x...y] = data[y + 1] - data[x];
// i作爲循環變量
// 維護data[i]的最小值mi,更新ans的最大值
// mi = min(mi, data[i])
// ans = max(data[i] - mi, ans)
// 可知若以第y個元素結尾的所有子列中list[x...y]爲最優解,那麼data[x]一定是data[1],data[2],data[3]...data[y](注意這裏沒有data[y+1],因爲至少要有一個元素)中的最小值

int main(int argc, char const *argv[])
{

    int T, N, sum, now, ans, min, minpos, l, r;
    scanf("%d", &T);
    for (int j = 1; j <= T; j++) {
        sum = 0;
        ans = -iinf;
        min = 0;
        minpos = 0;
        l = 1;
        r = 1;
        scanf("%d", &N);

        for (int i = 1; i <= N; i++) {
            scanf("%d", &now);
            sum += now;
            if (sum - min > ans) {//更新答案,這裏使用的是上一次循環的min值
                r = i;
                l = minpos + 1;
                ans = sum - min;
            }
            if (sum < min) {//該判斷語句不可和上一句調換,否則將可能出現最優解的數列長度爲0;
                min = sum;//更新本次循環的min,爲在下一循環中使用到
                minpos = i;
            }
        }

        if (j == 1) {
            printf("Case 1:\n");
        } else {
            printf("\nCase %d:\n", j);
        }

        printf("%d %d %d\n", ans, l, r);

    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章