Redraiment的走法

題目描述

Redraiment是走梅花樁的高手。Redraiment老是起點不限,從前到後,往高的樁子走,但走的步數最多,不知道爲何?
你能替Redraiment研究他最多走的步數嗎?

樣例輸入
    6
    2 5 1 5 4 5

樣例輸出
    3

提示
    Example:
    6個點的高度各爲 2 5 1 5 4 5
    如從第1格開始走,最多爲3步, 2 4 5
    從第2格開始走,最多隻有1步,5
    而從第3格開始走最多有3步,1 4 5
    從第5格開始走最多有2步,4 5
    因此這個結果是3。

輸入描述

輸入多行,先輸入數組的個數,再輸入相應個數的整數

輸出描述

輸出結果

輸入例子

6
2
5
1
5
4
5

輸出例子

3

算法實現

import java.util.Scanner;

/**
 * Declaration: All Rights Reserved !!!
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                a[i] = scanner.nextInt();
            }

            System.out.println(lsiEnhance(a));
        }

        scanner.close();
    }


    /**
     * 設f(i)表示L中以ai爲末元素的最長遞增子序列的長度。則有以下的遞推方程:
     * 這個遞推方程的意思是,在求以ai爲末元素的最長遞增子序列時,找到全部序
     * 號在L前面且小於ai的元素aj,即j<i且aj<ai。若是這樣的元素存在,那麼對
     * 全部aj,都有一個以aj爲末元素的最長遞增子序列的長度f(j),把其中最大的
     * f(j)選出來,那麼f(i)就等於最大的f(j)加上1,即以ai爲末元素的最長遞增
     * 子序列,等於以使f(j)最大的那個aj爲末元素的遞增子序列最末再加上ai;如
     * 果這樣的元素不存在,那麼ai自身構成一個長度爲1的以ai爲末元素的遞增子序列。
     *
     * @param a 待處理的數組
     * @return 最長遞增子序列長度
     */
    private static int lisSimple(int[] a) {
        int n = a.length;
        // 用於存放f(i)值;
        int[] f = new int[n];
        // 以第a1爲末元素的最長遞增子序列長度爲1;
        f[0] = 1;

        for (int i = 1; i < n; i++) {// 循環n-1次
            // f[i]的最小值爲1;
            f[i] = 1;
            for (int j = 0; j < i; j++) {// 循環i次
                if (a[j] < a[i] && f[j] > f[i] - 1) {
                    // 更新f[i]的值。
                    f[i] = f[j] + 1;
                }
            }
        }
        // 這個算法有兩層循環,外層循環次數爲n-1次,內層循環次數爲i次,
        // 算法的時間複雜度因此T(n)=O(n2)。
        return f[n - 1];
    }

    /**
     * 在上一種算法中,在計算每個f(i)時,都要找出最大的f(j)(j < i)來,因爲f(j)沒有順序,
     * 只能順序查找知足aj < ai最大的f(j),若是能將讓f(j)有序,就能夠使用二分查找,這樣算
     * 法的時間複雜度就可能降到O(nlogn)。因而想到用一個數組B來存儲「子序列的」最大遞增子
     * 序列的最末元素,即有B[f(j)] = aj在計算f(i)時,在數組B中用二分查找法找到知足
     * j < i且B[f(j)] = aj < ai的最大的j,並將B[f[j]+1]置爲ai。
     *
     * @param a
     * @return
     */
    private static int lsiEnhance(int[] a) {

        int n = a.length;
        // 數組B;
        float[] B = new float[n + 1];
        // 把B[0]設爲最小
        B[0] = Integer.MIN_VALUE;
        // 初始時,最大遞增子序列長度爲1的最末元素爲a1
        B[1] = a[0];
        // Len爲當前最大遞增子序列長度,初始化爲1;
        int len = 1;
        // p,r,m分別爲二分查找的上界,下界和中點;
        int p, r, m;

        for (int i = 1; i < n; i++) {
            p = 0;
            r = len;

            // 二分查找最末元素小於ai+1的長度最大的最大遞增子序列;
            while (p <= r) {
                m = (p + r) / 2;
                if (B[m] < a[i]) {
                    p = m + 1;
                } else {
                    r = m - 1;
                }
            }

            // 將長度爲p的最大遞增子序列的當前最末元素置爲ai+1;
            B[p] = a[i];
            if (p > len) {
                // 更新當前最大遞增子序列長度;
                len++;
            }


        }

        return len;
    }
}
相關文章
相關標籤/搜索