<style> html, body, .ui-content { background-color: #333; color: #ddd; } body > .ui-infobar { display: none; } .ui-view-area > .ui-infobar { display: block; } .markdown-body h1{ color: #9CCEF2; } .markdown-body h2{ color: #B1D6CA; } .markdown-body h3{ color: #F5F6B6; } .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: #ddd; } .markdown-body h1, .markdown-body h2 { border-bottom-color: #ffffff69; } .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { color: #fff; } .markdown-body img { background-color: transparent; } .ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a { color: white; border-left: 2px solid white; } .expand-toggle:hover, .expand-toggle:focus, .back-to-top:hover, .back-to-top:focus, .go-to-bottom:hover, .go-to-bottom:focus { color: white; } .ui-toc-dropdown { background-color: #333; } .ui-toc-label.btn { background-color: #191919; color: white; } .ui-toc-dropdown .nav>li>a:focus, .ui-toc-dropdown .nav>li>a:hover { color: white; border-left: 1px solid white; } .markdown-body blockquote { color: #bcbcbc; } .markdown-body table tr { background-color: #5f5f5f; } .markdown-body table tr:nth-child(2n) { background-color: #4f4f4f; } .markdown-body code, .markdown-body tt { color: #eee; background-color: rgba(230, 230, 230, 0.36); } a, .open-files-container li.selected a { color: #5EB7E0; } </style> ###### tags: `tgirc早修book` # 經典遞迴題型 本篇整理了幾種遞迴的經典題型。 ## 最大公因數 最大公因數通常是利用 [輾轉相除法](https://zh.wikipedia.org/wiki/%E8%BC%BE%E8%BD%89%E7%9B%B8%E9%99%A4%E6%B3%95) 的方式來實作。每次讓 a 與 b 兩者交替互相取餘數。 以下是 Top-down 的作法。 ```cpp= int GCD(int a, int b){ if(b == 0){ return a; } return GCD(b, a%b); } ``` Bottom-up的作法則是如下: ```cpp= while(b != 0){ int temp = a; a = b; b = temp % a; } cout << a; ``` ## 費波納契數列 費波納契數列,又稱費式數列。 該數列第零項為 0,第一、二項為 1,之後每一項都是其前兩項的和,也就是如下: ``` f(n) = 1 (n = 1 or 2) f(n) = f(n-1) + f(n-2) (n > 2) ``` Top-down 的作法如下: ```cpp= int fib(int n){ if( n==0) return 0; else if( n==1 || n==2 ) return 1; else return f(n-1) + f(n-2); } ``` Bottom-up的作法則是如下,一般會建立一個陣列來儲存所有值: ```cpp= int arr[n]; arr[0]=0; arr[1]=1; arr[2]=1; for(int i=3; i<n; i++){ arr[n] = arr[n-1] + arr[n-2]; } cout << arr[n]; ``` ## 字元排列組合 若有 n 個字元,輸出其所有排列方式。 假設今天的字元有 a, b, c 三個,把它的所有排列方式表示為 (a, b, c),那它的所有排列方式就是: ``` (a,b,c) = [a + (b,c)] + [b + (a,c)] + [a + (b,c)] ``` 也就是說,讓所有字元各擔任一次字串開頭,後面接剩下的字元的所有排列方式,而剩下的字元的所有排列方式又可用相同方式處理。 直到剩下一個字元時,單一個字元的排列方式只有一種,此時即可停止。 ```cpp= string str; void Perm(int i, int n){ if(i == n){ cout << str << "\n"; } else{ for(int j=i; j<n; j++){ swap(str[i], str[j]); //把j字元換到開頭 Perm(i+1, n); //剩下的字元作排列組合 swap(str[i], str[j]); //把j字元放回原位 } } } ``` ## 河內塔 河內塔是一個大盤子在下,小盤子在上,總共三根柱子可以放盤子的裝置。 目標是將 A 柱子上的所有盤子按照順序移到 C 柱子上,一次只能移一個盤子,且大盤子不能放在小盤子上,按照順序輸出移動步驟。 首先觀察只有 2 個盤子的情況,先將小盤子由 A 移到 B,再把大盤子由 B 移到 C ,最後把小盤子由 B 移到 C。 其實可以想成是把最大的盤子以上的小盤子全部移到 B 柱子上,再把最底下的移到 C 柱子,最後把 B 柱子的盤子全部移到 C 柱子上。 此時我們把 A 稱為起始柱,B 為中間柱作為轉運站,C 則是終點柱。所有的 n 層塔都可拆為以下步驟: 1. 將 起始柱 上 n-1 個盤子移到 中間柱 2. 將 起始柱 最底下剩餘的 1 個盤子移到 終點柱 3. 將 中間柱 上 n-1 個盤子移到 終點柱 而 n-1 個盤子的移動方式,又可按照這個步驟執行下去。 移動至剩下一個盤子時,就只需將那個盤子直接由起點柱移到終點柱。 :::spoiler <font color="F5F6B6">**流程圖**</font> ![](https://i.imgur.com/zdpN8pw.png) ![](https://i.imgur.com/u3xBOoO.png) ![](https://i.imgur.com/557VoBL.png) ![](https://i.imgur.com/vsgegvc.png) ![](https://i.imgur.com/kW7cUr8.png) ![](https://i.imgur.com/B2AjsUp.png) ![](https://i.imgur.com/8hmkOEr.png) ::: > ```cpp= void Hanoi(int n, char A, char B, char C){ //把n個盤子由A柱子移至C柱子 //A為起點柱,B為中間柱,C為終點柱 if( n==1 ){ cout << A << " -> " << C; } //如果只有一個盤子,就直接由起點柱移到終點柱 else{ Hanoi(n-1, A, C, B); //將n-1個盤子由起點柱移到中間柱 Hanoi(1, A, B, C); //將最底下的1個盤子由起點柱移到終點柱 Hanoi(n-1, B, A, C); //將n-1個盤子由中間柱移到終點柱 } } ```