1. 程式人生 > >動態規劃系列專題講義之斐波那契數列

動態規劃系列專題講義之斐波那契數列

動態規劃系列專題講義

專題一:斐波那契數列

/*

 Name: 動態規劃專題之斐波那契數列 

 Copyright:  巧若拙

 Author: 

 Date: 22-03-17 08:56

 Description: 1755_菲波那契數列

描述:斐波那契數列是指這樣的數列: 數列的第一個和第二個數都為1,接下來每個數都等於前面2個數之和。給出一個正整數a,要求斐波那契數列中第a個數是多少。

輸入:第1行是測試資料的組數n,後面跟著n行輸入。每組測試資料佔1行,包括一個正整數a(1<= a <= 20)

輸出:輸出有n行,每行輸出對應一個輸入。輸出應是一個正整數,為菲波那契數列中第a個數的大小

樣例輸入

4

5

2

19

1

樣例輸出

5

1

4181

1

*/ 

#include<iostream> 

#include<cmath> 

using namespace std; 

const int MAXN = 50; 

int F1[MAXN];//Fibonacci數列

int F2[MAXN] = {0, 1};//Fibonacci數列 

int Fibonacci(int n); //遞迴演算法

int Fibonacci_1(int n); //備忘錄:自頂而下 

int Fibonacci_2(int n);//動態規劃:自底而上 

int Fibonacci_3(int n);//動態規劃:降維優化

int main()  

   int n, a; 

       Fibonacci_2(MAXN);//動態規劃,先記錄所有子問題的解

       cin>> n;

       for(int i=0; i<n; i++) 

   { 

              cin>> a;

       cout << Fibonacci(a) << endl;

       cout << Fibonacci_1(a) << endl;

       cout << F2[a] << endl;

           cout << Fibonacci_3(a) <<endl;

   } 

    return0; 

演算法1:遞迴演算法,沒有記錄任何中間結果。

int Fibonacci(int n)

   if (n == 0 || n == 1)  //遞迴出口

   { 

       return    //語句1

   } 

   return Fibonacci( ) + Fibonacci( ); //語句2

問題1:將語句1和語句2補充完整。

參考答案:

問題1:語句1:return n;

      語句2:return Fibonacci(n-1) +Fibonacci(n-2);

演算法2:備忘錄演算法:自頂而下,需要用到全域性變數F1 [MAXN]。

int Fibonacci_1(int n)  

   if (F1[n] > 0)  //如果這個問題曾經計算過,直接返回  

   { 

       return   //語句1

    }

       if(n == 0 || n == 1)  //遞迴出口

   { 

       F1[n] =   //語句2

   }  

    else

    {

              F1[n]=    //語句3

       }

   return F1[n];

問題1:將語句1,語句2和語句3補充完整。

問題2:與演算法1(遞迴演算法)相比,演算法2(備忘錄演算法)有哪些優越之處?

參考答案:

問題1:語句1:return F1[n];

      語句2:F1[n] = n;

語句3:F1[n] = Fibonacci_1(n-1) + Fibonacci_1(n-2);

問題2:遞迴演算法進行了重複計算,而備忘錄演算法利用一維陣列F1[n]記錄了子問題的解,無需重複計算,大大提高了效率。

演算法3:動態規劃:自底而上,需要用到全域性變數int F2[MAXN] ={0, 1};。

int Fibonacci_2(int n)

   for (int i=2; i<=n; i++) 

   { 

       F2[i] =   //語句1

   } 

   return F2[n]; 

問題1:將語句1補充完整。

問題2:與演算法2(備忘錄演算法)相比,演算法3(動態規劃)有哪些異同?

參考答案:

問題1:語句1:F2[i] = F2[i-1] + F2[i-2];

問題2:備忘錄和動態規劃演算法都是利用遞推表示式獲得子問題的解,並記錄了子問題的解,用空間換時間,提高了時間效率。但是二者的思考方向不同,備忘錄演算法是自頂而下,從最終解出發,採用遞迴的方式來求解;動態規劃演算法是自底而上,從最小的子問題出發,逐步向上求出較大問題的解,直到獲得最終解。

演算法4:/動態規劃:降維優化,使用3個變數代替一維陣列。

int Fibonacci_3(int n)

       intcur, pre1, pre2;

       pre1= 0, cur = pre2 = 1; //初始化

   for (int i=2; i<=n; i++) //自底向上,迭代更新變數值

   { 

              cur=     //語句1

       pre1 =    //語句2

       pre2 =    //語句3

   } 

   return cur; 

問題1:將語句1,語句2和語句3補充完整。

問題2:與演算法3(基本動態規劃演算法)相比,演算法4(動態規劃降維優化)有哪些異同?

參考答案:

問題1:語句1:cur = pre1 + pre2; 

      語句2:pre1 = pre2;

語句3:pre2 = cur;

問題2:二者同屬動態規劃演算法,都利用額外的變數(或陣列)記錄了各個子問題的解,都是從最小的子問題出發,逐步向上求出較大問題的解,直到獲得最終解。演算法3保留了所有子問題的解,雖然需要較多的空間,但是一次計算之後,就可以直接輸出任意解了;演算法4利用斐波那契數列遞推公式的特性,只保留了每個元素的當前值和它前面的2個元素值,對計算過程中產生的子問題解用過即棄,所需空間較少,但每次求解新元素的值,都需要從頭開始計算,適用於只需要求某一個元素值的情形。

課後練習:

練習1:1788_Pell數列

描述:Pell數列a1, a2, a3, ...的定義是這樣的,a1 = 1, a2 = 2, ... , an = 2* an-1 + an - 2 (n > 2)。

給出一個正整數k,要求Pell數列的第k項模上32767是多少。

輸入:第1行是測試資料的組數n,後面跟著n行輸入。每組測試資料佔1行,包括一個正整數k (1 ≤ k < 1000000)。

輸出:n行,每行輸出對應一個輸入。輸出應是一個非負整數。

樣例輸入

2

1

8

樣例輸出

1

408

練習2:3089_爬樓梯

描述:樹老師爬樓梯,他可以每次走1級或者2級,輸入樓梯的級數,求不同的走法數。

例如:樓梯一共有3級,他可以每次都走一級,或者第一次走一級,第二次走兩級也可以第一次走兩級,第二次走一級,一共3種方法。

輸入:輸入包含若干行,每行包含一個正整數N,代表樓梯級數,1<= N <= 30

輸出:不同的走法數,每一行輸入對應一行輸出

樣例輸入

5

8

10

樣例輸出

8

34

89

練習3:2046_骨牌鋪方格

描述:在2×n的一個長方形方格中,用一個1× 2的骨牌鋪滿方格,輸入n ,輸出鋪放方案的總數。例如n=3時,為2× 3方格,骨牌的鋪放方案有三種,如下圖:

輸入:輸入資料由多行組成,每行包含一個整數n,表示該測試例項的長方形方格的規格是2×n(0<n<=50)。

輸出:對於每個測試例項,請輸出鋪放方案的總數,每個例項的輸出佔一行。

樣例輸入

1

3

2

樣例輸出

1

3

2

練習4:2718_移動路線

描述:桌子上有一個m行n列的方格矩陣,將每個方格用座標表示,行座標從下到上依次遞增,列座標從左至右依次遞增,左下角方格的座標為(1,1),則右上角方格的座標為(m,n)。

小明是個調皮的孩子,一天他捉來一隻螞蟻,不小心把螞蟻的右腳弄傷了,於是螞蟻只能向上或向右移動。小明把這隻螞蟻放在左下角的方格中,螞蟻從左下角的方格中移動到右上角的方格中,每步移動一個方格。螞蟻始終在方格矩陣內移動,請計算出不同的移動路線的數目。

對於1行1列的方格矩陣,螞蟻原地移動,移動路線數為1;對於1行2列(或2行1列)的方格矩陣,螞蟻只需一次向右(或向上)移動,移動路線數也為1……對於一個2行3列的方格矩陣,如下圖所示:

-------------------

|(2,1)|(2,2)|(2,3)|

-------------------

|(1,1)|(1,2)|(1,3)|

-------------------

螞蟻共有3種移動路線:

路線1:(1,1) → (1,2) → (1,3) → (2,3)

路線2:(1,1) → (1,2) → (2,2) → (2,3)

路線3:(1,1) → (2,1) → (2,2) → (2,3)

輸入

輸入只有一行,包括兩個整數m和n(0<m+n<=20),代表方格矩陣的行數和列數,m、n之間用空格隔開

輸出

輸出只有一行,為不同的移動路線的數目。

樣例輸入

2 3

樣例輸出

3

提示:移動路線類似爬樓梯問題,但不是一維路徑,而是二維路徑,可以使用二維陣列來記錄到各個位置的路線數量。熟練掌握基本的動態規劃演算法後,可以考慮實現降維優化。

pan � f)=Np��ج → (1,2) → (2,2) → (2,3)

路線3:(1,1) → (2,1) → (2,2) → (2,3)

輸入

輸入只有一行,包括兩個整數m和n(0<m+n<=20),代表方格矩陣的行數和列數,m、n之間用空格隔開

輸出

輸出只有一行,為不同的移動路線的數目。

樣例輸入

2 3

樣例輸出

3

提示:移動路線類似爬樓梯問題,但不是一維路徑,而是二維路徑,可以使用二維陣列來記錄到各個位置的路線數量。熟練掌握基本的動態規劃演算法後,可以考慮實現降維優化。