N級台階走法(算法問題)


N級台階走法

題目: 總共100級台階(任意N級都行),小明每次可選擇走1步、2步,問走完這100級台階總共有多少種走法?

分析:對於台階走法

假設只有一個台階,那么只有一種跳法,那就是一次跳一級,f(1)=1;如果有兩個台階,那么有兩種跳法,第一種跳法是一次跳一級,第二種跳法是一次跳兩級, 如果有大於2級的n級台階,那么假如第一次跳一級台階,剩下還有n-1級台階,有f(n-1)種跳法,假如第一次條2級台階,剩下n-2級台階,有f(n-2)種跳法。

​ 只有1階時,有F(1) -> 1;

​ 2階時,有F(2) -> 2 種走法;

​ 3階時,有F(3) = F(1) + F(2) 種走法;

​ ```

​ N階時,有F(N) = F(N-1) + F(N-2) 種走法;

可知為斐波那契數列。對於代碼實現,可以通過遞歸思想來實現;

//走台階的基礎走法oneStep_lv0()方法
private static long oneStep_lv0(long stpes){
        if(stpes < 1){
            return 0;
        }
        if(stpes == 1){
            return 1;
        }
        if(stpes == 2){
            return 2;
        }
        return oneStep_lv0(stpes-1)+oneStep_lv0(stpes-2);
}
//調用 oneStep_lv0()方法
 private static void doAnswer(int stpes){
		System.out.print(oneStep_lv0(stpes));
 }

分析:此算法簡單粗暴,通過遞歸調用解決走台階問題,但分析我們會發現 當F(100)時會計算F(99) 和F(98),而計算F(99)時 又需要計算F(98)、F(97) ,F(98) 就會重復計算,實際執行代碼時我們會發現,計算的耗時會非常之久,為了避免這個問題,我們可以用空間換時間:

既然計算F(100) 必須要 計算 F(98)+F(99),那么意味着,我其實計算F(N)時,需要將之前的 N-1種情況都計算了,那么我們可以由低階往高階計算,並將每次結果存儲起來,優化代碼如下:

 private static void doAnswer(int stpes){
     //定義數組,存放每次走台階對應的步數
        long[] answerArr = new long[stpes+1];
        for (int i = 1; i <= stpes ; i++) {
            //從第1階開始,循環調用
            answerArr[i] = oneStep_lv1(i,answerArr);
        }
        System.out.print(answerArr[stpes]);
    }

//走台階的基礎走法oneStep_lv1()方法
private static long oneStep_lv1(int stpes,long[] answerArr){
        if(answerArr[stpes] != 0){
            //台階之前走過,則直接返回上次計算結果
            return answerArr[stpes];
        }
        if(stpes == 1){
            return 1;
        }
        if(stpes == 2){
            return 2;
        }
        return oneStep(stpes-1,answerArr) + oneStep(stpes-2,answerArr);
    }

改進后計算時間將大大減少。

注意由於100階的結果是 573147844013817084101 會溢出 long 的最大長度,所以需要將結果換為BigDecimal,在此省略改造代碼。

衍生:

第二種解法其實用到了動態規范的相關思想:

動態規划原理

  • 基本思想:問題的最優解如果可以由子問題的最優解推導得到,則可以先求解子問題的最優解,在構造原問題的最優解;若子問題有較多的重復出現,則可以自底向上從最終子問題向原問題逐步求解。
  • 使用條件:可分為多個相關子問題,子問題的解被重復使用
    • Optimal substructure(優化子結構):
      • 一個問題的優化解包含了子問題的優化解
      • 縮小子問題集合,只需那些優化問題中包含的子問題,降低實現復雜性
      • 我們可以自下而上的
    • Subteties(重疊子問題):在問題的求解過程中,很多子問題的解將被多次使用。
  • 動態規划算法的設計步驟:
    • 分析優化解的結構
    • 遞歸地定義最優解的代價
    • 自底向上地計算優化解的代價保存之,並獲取構造最優解的信息
    • 根據構造最優解的信息構造優化解
  • 動態規划特點:
    • 把原始問題划分成一系列子問題;
    • 求解每個子問題僅一次,並將其結果保存在一個表中,以后用到時直接存取,不重復計算,節省計算時間
    • 自底向上地計算。
    • 整體問題最優解取決於子問題的最優解(狀態轉移方程)(將子問題稱為狀態,最終狀態的求解歸結為其他狀態的求解)

通俗點,動態規划是把一個大問題拆解成一堆小問題,這個本身沒啥問題,但是這個不是動態規划的核心思想,或者說,一個”大問題“之所以能用”動態規划“解決,並不是因為它能拆解成一堆小問題,因為任何大問題都能拆解成小問題.

取決於該問題是否能用動態規划解決的是這些”小問題“會不會被被重復調用


免責聲明!

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



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