華爲oj初級題目——合唱隊

原題:

原題說了知識點是循環,雖然也正確,但成功誤導了大批像我一樣的菜鳥們。此外圖片中描述中的滿足條件應該是T1<T2<......<Ti-1<Ti>Ti+1>......>TK

此題正確的解題思想是動態規劃。

先說說我的錯誤思路吧,可能有很多人跟我的思路一樣,卻還沒明白錯在哪裏。
我的(錯誤)思路:以第i(0<=i<=n)個人爲中心,分別向兩邊遍歷,剔除比上一個高的人,加起來得到需要出列的同學總人數。n次循環後保留最小值。

舉個列子:假設同學的身高:17,18,19,10,20,19,18。(不要吐槽都是矮人)
很明顯正確答案是1。只需要身高爲10的同學出列即可,然而按照算法,會將17,18,19都出列。顯然會得到錯誤的答案。

正確思路:以第i(0<=i<=n)個人爲中心,分別從兩邊向i遍歷,求出最長上升序列。

不就是換了個遍歷方向嗎?有什麼不同。這就要從動態規劃的基本要素說起了(其實沒說,不要打我)。

動態規劃的基本要素:1.最優子結構 2.重疊子問題 (不懂的自己解決吧,我就不獻醜了)

飲水思源,我的代碼思(源)路(碼)是從這裏來的。

針對變量的解釋:n:總人數;a:身高數組;a1:從0到i的最長子序列長度;a2:從n-1到i的最長子序列長度。比如:a[3]=2,表示從0到3號人,滿足身高要求的最長序列長度是2,即至少出列2人使得剩下的人滿足身高要求。至於出列哪兩個人,動態規劃算法無法解決。

import java.util.Scanner;

public class Chorus {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n;
        int[] a, a1, a2;
        n = input.nextInt();
        a = new int[n];
        a1 = new int[n];
        a2 = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = input.nextInt();
        }

        for (int i = 0; i < n; i++) {
            a1[i] = 1;// 初始值置爲1,因爲最小是1,即他自己
            // 求出到i的最長子序列
            // 外層循環好懂,內層循環我來稍稍解釋一下,它的作用是找到i之前最長的子序列,但是要滿足i的身高大於j的身高,a1[i]就等於那個最長子序列+1(自身)
            // 可能還不好懂,建議沒懂的同學找張紙劃一下
            for (int j = 0; j < i; j++) {
                if (a[i] > a[j] && a1[i] < a1[j] + 1) {
                    a1[i] = a1[j] + 1;
                }
            }
        }
        // 同理
        for (int i = n - 1; i >= 0; i--) {
            a2[i] = 1;
            // 求出到i的最長子序列
            for (int j = n - 1; j > i; j--) {
                if (a[i] > a[j] && a2[i] < a2[j] + 1) {
                    a2[i] = a2[j] + 1;
                }
            }
        }

        int max = 0;
        for (int i = 0; i < n; i++) {
            // 注意第i個人被加了2次
            if (max < a1[i] + a2[i] - 1) {
                max = a1[i] + a2[i] - 1;
            }
        }
        System.out.println(n - max);
    }
}

代碼通過了華爲oj的測試用例,但我不能保證代碼的正確性,如果您看出了我的代碼有錯誤之處,歡迎指正。

8 186 186 150 200 160 130 197 200 
4

內層循環的解釋:
放在底部是因爲我太笨了,寫給我自己看的。
以測試用例爲例:
186 186 150 200 160 130 197 200
下面的分析步驟是按照人的思維來的,跟代碼本質是一樣的,區別在於代碼是按步執行的,而人的思維具有跳躍性。
首先有了a數組,a1,a2數組初始化都爲1。
先計算a1:
a[0]=186, a1[0]=1;不用說。
a[1]=186, a1[1]=1;找到滿足a[i]>a[k]且a1[k]最大的那個k,顯然沒找到,保持默認值1。
a[2]=150, a1[2]=1;與上面相同的做法。
a[3]=200, a1[3]=2;找到滿足條件的k值,k=2,那麼a1[3]=a1[2]+1;
a[4]=160, a1[4]=2;
a[5]=130, a1[5]=1;
a[6]=197, a1[6]=3;
a[7]=200, a1[7]=4;

計算a2:
先計算a2[7]
a2[7]=1;
a2[6]=1;
a2[5]=1;
a2[4]=2;
a2[3]=3;
a2[2]=2;
a2[1]=3;
a2[0]=3;

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