ACM—最大連續子序列(HDOJ1003)


HDOJ鏈接 http://acm.hdu.edu.cn/showproblem.php?pid=1003 不了解題目的朋友可以先看一下題目,在這里就不再詳細介紹了。(文章內容和解題思路不完全相同,方法一、二、三、四沒有對sequence 全為負數的情況進行考慮,就不再對代碼進行更新了,如果需要可看1003解題代碼,最下面。)

Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub-sequence.

For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14.

下面就直接進行分析:

 方法一:暴力破解,時間復雜度:O(n^3)

  對所有情況的子串進行計算,然后求出和最大的子串。這個就不詳細解釋了看代碼就能明白。

private void test01() {
        int maxValue = 0;
        for (int i = 0; i < array.length; i++) {// 第一次循環
            for (int j = i; j < array.length; j++) {// 第二次循環
                int thisMaxValue = 0; // 這里置零
                for (int k = j; k < array.length; k++) {// 開始新的循環計算thisMaxValue
                    thisMaxValue += array[k];
                }
                if (thisMaxValue > maxValue) {
                    maxValue = thisMaxValue;
                }
            }
        }
        System.out.println(maxValue);
    }

 

 方法二:還是暴力破解,時間復雜度:O(n^2) 。需要注意的是和方法一的區別。

public void test02() {
        int maxValue = 0;
        for (int i = 0; i < array.length; i++) {// 第一次循環
            int thisMaxValue = 0;
            for (int j = i; j < array.length; j++) {// 第二次循環
                thisMaxValue += array[j]; // 這里沒有置零,利用了上個thisMaxValue數值
                if (thisMaxValue > maxValue) {
                    maxValue = thisMaxValue;
                }
            }
        }
        System.out.println(maxValue);
    }

 

  區別:方法一每次都是對子串全部循環一遍,而方法二,利用了第二層循環,不再對子串進行全部循環。

 方法三:分治算法,遞歸求解。時間復雜度:O(nlogn)

public void test03() {
        System.out.println(start(array, 0, array.length / 2, array.length - 1));
    }
    //遞歸方法
    private int start(int[] array, int left, int mid, int right) {
        if (left == right) {
            return array[left];
        }
        int leftMaxValue = start(array, left, (left + mid) / 2, mid);// 遞歸求左子串的最大值
        int rightMaxValue = start(array, mid + 1, (mid + right) / 2, right);// 遞歸求右子串的最大值
        /**
          開始計算跨兩邊的最大子序列
       toLeftMaxValue <—— MaxSum(mid to left)
     toRightMaxValue<—— MaxSum(mid to right)
       midMaxValue = toLeftMaxValue +toRightMaxValue;
**/ int toLeftMaxValue = 0; int tempMaxValue = 0; for (int i = mid; i >= 0; i--) { tempMaxValue += array[i]; if (tempMaxValue > toLeftMaxValue) { toLeftMaxValue = tempMaxValue; } } tempMaxValue = 0; int toRightMaxValue = 0; for (int i = mid + 1; i <= right; i++) { tempMaxValue += array[i]; if (tempMaxValue > toRightMaxValue) { toRightMaxValue = tempMaxValue; } } //計算出跨左右兩邊的最大子串和 int midMaxValue = toRightMaxValue + toLeftMaxValue; //返回本層循環的最大子串和 return Math.max(Math.max(leftMaxValue, midMaxValue), rightMaxValue); }

 

需要好好考慮的是為何在計算誇兩邊最大子串和的時候需要 toRightMaxValue + toLeftMaxValue,考慮明白這個問題,方法三也就明白了。因為 toLeftMaxValue 的子串和 toRightMaxValue 的子串是連接着的,其節點就是mid,所以兩者完全可以進行相加以求出跨兩邊的最大子串和。

  方法四:遍歷累積。時間復雜度:O(n)。

    public void test04() {
        int maxValue = 0;
        int thisMaxValue = 0;
        for (int i = 0; i < array.length; i++) {
            thisMaxValue += array[i];
            if (thisMaxValue > 0) {
                maxValue = thisMaxValue > maxValue ? thisMaxValue : maxValue;
            } else { // 當子串不大於零的時候,子串斷裂,開始新的子串。
                thisMaxValue = 0;
            }
        }
        System.out.println(maxValue);
    }

 

  不再解釋。

  后面還有兩種方法沒有實現。有興趣的可以參考這里:http://blog.csdn.net/samjustin1/article/details/52043369

 

1003的代碼更新上:

import java.util.Scanner;

public class Main {

    static int maxValue;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int num = in.nextInt();
        int tip = 1;
        while (in.hasNextInt()) {
            String[] datas = in.nextLine().split(" ");
            if (!"".equals(datas[0])) {
                getMaxValue(tip++, datas);
                if (tip > num) {
                    break;
                }
                System.out.println();
            }
        }

    }

    private static void getMaxValue(int num, String[] array) {
        boolean isAllNegative = true, isFirstPositive = true;//是否都為負數    是否是第一個正數
        int maxValue = 0;    //最終的最大值
        int start = 1, end = 1, thisStart = 1, thisEnd = 1;//最終子串的起始為止 最終的子串結束為止  本次循環的起始位置 本次循環的結束位置
        int thisMaxValue = 0;    //本次循環的最大值
        for (int i = 1; i < array.length; i++) {
            if (isAllNegative) {//如果該子串都是負數則不進行累加,負數越加越小
                thisMaxValue = Integer.parseInt(array[i]);
            } else {//如果包含正數則進行累加
                thisMaxValue += Integer.parseInt(array[i]);
            }
            if (thisMaxValue > 0) {//本次循環的子串大於零則需要和前面的子串進行相加
                if (thisMaxValue > maxValue) {//循環子串大於最大值則把本次循環的結果進行保存
                    isAllNegative = false;    //此時,整個串必定包含正數,所以改變標識符
                    maxValue = thisMaxValue;
                    thisEnd = i;
                    if (isFirstPositive) {
                        thisStart = i;
                        isFirstPositive = false;
                    }
                    start = thisStart;
                    end = thisEnd;
                }
            } else if (thisMaxValue < 0) {//本次循環結果小於零時
                if (isAllNegative) {//都是負數則對maxValue、start和end進行更新
                    if (i == 1) {
                        maxValue = Integer.parseInt(array[i]);
                    }
                    if (maxValue < Integer.parseInt(array[i])) {
                        start = i;
                        end = i;
                        maxValue = Integer.parseInt(array[i]);
                    }
                } else {//此時新的子串誕生,記錄下本子串的起始位置。
                    thisMaxValue = 0;
                    thisStart = i + 1;
                }
            }
        }
        System.out.println("Case " + num + ":");
        System.out.println(maxValue + " " + start + " " + end);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM