文章目錄
- 青蛙跳臺階
- 題目
- 思路
- 分析
- 1. 從跳法次數分析
- 代碼1(遞回)
- 2. 從程序分析
- 代碼2(非遞回)
- 青蛙跳臺階變式1
- 題目
- 分析
- 代碼3(遞回)
- 青蛙跳臺階變式2
- 題目
- 分析
- 代碼4(遞回)
- 漢諾塔問題(求步數)
- 題目
- 思路
- 分析
- 代碼5(非遞回)
- 代碼6(遞回)
- 漢諾塔問題(求移動程序)
- 題目
- 思路
- 分析
- 代碼7(遞回)
- 結語
青蛙跳臺階
題目
一只青蛙一次可以跳上1級臺階,也可以跳上2級臺階,求該青蛙跳上一個 n 級的臺階總共有多少種跳法
思路
遇見題目我們可以在紙上先動手畫畫,把最簡單的幾種方式列出來,作比較,找規律,
| 臺階數n | 跳法次數 | 程序 |
|---|---|---|
| 1 | 1 | [1] |
| 2 | 2 | [1,1],[2] |
| 3 | 3 | [1,1,1],[2,1],[1,2] |
| 4 | 5 | [1,1,1,1],[2,1,1],[1,2,1],[1,1,2],[2,2] |
| 5 | 8 | [1,1,1,1,1],[2,1,1,1],[1,2,1,1],[1,1,2,1],[1,1,1,2],[2,2,1],[2,1,2],[1,2,2] |
| … | … | … |
分析
按照上面表格可以從跳法次數,程序,或者兩者結合找規律
1. 從跳法次數分析
- 觀察表格,可以知道從n>=3時,第n個數就是前兩個數的和(與斐波那契數列一樣)
- 我們自己推論,當臺階數為n時,設跳法有f(n)次,如果青蛙先跳1階,則剩下的臺階數為n-1,即剩余跳法有f(n-1)次;如果青蛙先跳2階,則剩下的臺階數為n-2,即剩余跳法有f(n-2)次,
- 故跳法次數f(n)=f(n-1)+f(n-2),因為等號右邊有兩個值,故當n=1,n=2時為最后的特殊限制條件
- 下面代碼為遞回求法,如果想用非遞回,可以將遞回通項改成回圈
代碼1(遞回)
#include <stdio.h>
int jump(int n)
{
if (n == 1)
return 1;
if (n == 2)
return 2;
return jump(n - 1) + jump(n - 2);
}
int main()
{
int n;
scanf("%d", &n);
int ret = jump(n);
printf("%d", ret);
return 0;
}
2. 從程序分析
- 觀察表格,可以知道,跳n階臺階,跳兩階臺階次數可以為0到n/2次,而每一次跳兩階臺階的順序也是不定的,可以通過計數原理的組合數C(n,m),表示從n個數中選m個數排列,n表示每次需要跳的次數,m表示一次跳兩階的次數
- 組合數C(n,m),可以由n!/(m!*(n-m)!)求得
- 下面代碼為非遞回求法,如果想要寫成遞回,可以根據回圈修改
代碼2(非遞回)
#include <stdio.h>
int fac(int m)
{
int i = 0;
int count = 1;
for (i = 1; i <= m; i++)
{
count *= i;
}
return count;
}
int jump(int n)
{
int i = 0; //i為跳兩階臺階的次數
int sum = 0; //sum為計算跳法
for (i = 0; i <= n / 2; i++)
{
int a = 0;
a = n - i * 2 + i; //a為跳到n階臺階跳的次數
sum += fac(a) / (fac(i)*fac(a - i));
}
return sum;
}
int main()
{
int n;
scanf("%d", &n);
int ret = jump(n);
printf("%d", ret);
return 0;
}
青蛙跳臺階變式1
題目
一只青蛙一次可以跳上1級臺階,也可以跳上2級臺階…也可以跳n級臺階,求該青蛙跳上一個 n 級的臺階總共有多少種跳法
分析
- 根據原題推論,當臺階數為n時,設跳法有f(n)次,如果青蛙先跳1階,則剩下的臺階數為n-1,即剩余跳法有f(n-1)次;如果青蛙先跳2階,則剩下的臺階數為n-2,即剩余跳法有f(n-2)次,
- 那么當青蛙跳3階臺階,則剩下的臺階數為n-3,即剩余跳法有f(n-3)次…當青蛙跳n階臺階,則剩下的臺階數為n-n,即剩余跳法有f(n-n)次
- 故跳法次數f(n)=f(n-1)+f(n-2)+f(n-3)…+f(n-n)
- 由推論可得f(n-1)=f(n-2)+f(n-3)…+f(n-n),將其代入上面式子
- 故跳法次數為f(n)=2*f(n-1),因為等號右邊只有一個值,故n=1為最后的特殊限制條件
代碼3(遞回)
#include <stdio.h>
int jump(int n)
{
if (n == 1)
return 1;
return 2*jump(n - 1);
}
int main()
{
int n;
scanf("%d", &n);
int ret = jump(n);
printf("%d", ret);
return 0;
}
青蛙跳臺階變式2
題目
一只青蛙一次可以跳上1級臺階,也可以跳上2級臺階…也可以跳m級臺階,求該青蛙跳上一個 n 級的臺階總共有多少種跳法(m<=n)
分析
- 根據變式1推論得f(n)=f(n-1)+f(n-2)+f(n-3)…+f(n-n)
- 而這里最多一次只能跳m階,故f(n)=f(n-1)+f(n-2)+f(n-3)…+f(n-m)
- 由推論得f(n-1)=f(n-2)+f(n-3)…+f(n-m)+f(n-m-1),代入上面式子
- 故跳法次數為f(n)=2*f(n-1)-f(n-m-1)
- 因為通過遞回n的值在減少,當n<m時,其實最多就只能跳n階,與變式1就是一樣的問題了
代碼4(遞回)
#include <stdio.h>
int jump(int n,int m)
{
if (n > m)
return 2 * jump(n - 1, m) - jump(n - 1 - m, m);
else
{
if (n == 1)
return 1;
return 2 * jump(n - 1, n);
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
int ret = jump(n,m);
printf("%d", ret);
return 0;
}
漢諾塔問題(求步數)
題目
有A,B,C三個柱子,A柱子上從上到下,從小到大排列著n個圓盤,現要求將A柱子上的n個圓盤全部移動到C柱子上,依然按照從上到下,從小到大的順序排列,且對移動程序要求如下:
a)一次只能移動一個盤子,
b)移動程序中大盤子不允許出現在小盤子上方,
問:總共需要移動的步數是多少?
思路
因為求的是步數,我們可以通過找前面幾組資料,觀察是否有什么規律
| 盤子數n | 步數 |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 7 |
| 4 | 15 |
| 5 | 31 |
| … | … |
分析
- 通過表格觀察,可以知道盤子數為n時,步數為20+21+…+2n-1,即2n-1
- 我們可以通過下面這張圖片來推論:
- 假設盤子數量為n,通過化繁為簡思想,我們可以把盤子分成兩個部分,上面n-1個盤子,和最下面一個盤子,移動步驟如下:
- 將最上面的n-1個盤子移動到B柱上
- 將最下面的盤子移動到C柱上
- 再將B柱上的n-1個盤子移動到C柱上
- 問題轉化成如何移動最上面n-1個盤子,按照上面的思路解決n-1個盤子移動的問題,
- 假設移動n個盤子需要的步數為f(n),則移動n-1個盤子需要f(n-1)步,
- 故移動步數為f(n)=f(n-1)+1+f(n-1),即f(n)=2*f(n-1)+1
- 通過等比數列變形又可以得到f(n)=2n-1
代碼5(非遞回)
#include <stdio.h>
#include <math.h>
int main()
{
int n;
scanf("%d", &n);
int count =0;
count=(int)pow(2,n)-1;
printf("%d", count);
return 0;
}
代碼6(遞回)
#include <stdio.h>
int tower(int n)
{
if (n == 1)
return 1;
else
return 2 * tower(n - 1) + 1;
}
int main()
{
int n;
scanf("%d", &n);
int ret=tower(n);
printf("%d", ret);
return 0;
}
漢諾塔問題(求移動程序)
題目
有A,B,C三個柱子,A柱子上從上到下,從小到大排列著n個圓盤,現要求將A柱子上的n個圓盤全部移動到C柱子上,依然按照從上到下,從小到大的順序排列,且對移動程序要求如下:
a)一次只能移動一個盤子,
b)移動程序中大盤子不允許出現在小盤子上方,
問:列印移動的方案 (例如, 移動A柱最上面的圓盤到C柱, 則輸出"A -> C")
思路
因為求的是移動方案,所以我們可以將前幾組資料列出來,結合遞回化簡為繁的思想找共性和非共性
| 盤子數n | 程序 |
|---|---|
| 1 | A->C |
| 2 | A->B,A-C,B->C |
| 3 | A->C,A->B,C->B,A->C,B->A,B->C,A->C |
| 4 | A->B,A->C,B->C,A->B,C->A,C->B,A->B,A->C,B->C,B->A,C->A,B->C,A->B,A->C,B->C |
| … | … |
分析
- 通過觀察得到:除了n=1,n>1時,都是先將A柱上面n-1個盤子拿到B柱(粗字體為其程序),再將A柱最下面盤子拿到C柱,此時A柱變成輔助柱,再將B柱上的盤子放到C柱
- 故將A柱最下面盤子移到C柱為中間程序
- 上一步為將初始柱(A柱)上面n-1個盤子借助輔助柱(C柱)移到目標柱(B柱)【其實可以這里看作單獨一個n-1的漢諾塔,將A柱上的盤子移動到B柱】
- 下一步為將初始柱(B柱)上面n-1個盤子借助輔助柱(A柱)移到目標柱(C柱)【其實可以這里看作單獨一個n-1的漢諾塔,將B柱上的盤子移動到C柱】
- 而上一步,中間程序,下一布就是遞回的核心思想
- 而當n=1時,盤子數只有一個,我們將其直接放到目標柱即可(其為最終的限制條件)
- 初始柱,輔助柱,目標柱,其實就是把該步驟的移動程序當作一個單獨的漢諾塔問題,需要移動盤子現在所在的位置為初始柱,要將其放到的位置就是目標柱
代碼7(遞回)
#include <stdio.h>
void hanio(int n, char x, char y, char z)
{
if (n == 1)
printf("%c->%c\n",x,z); //當盤子只剩一個時,直接列印初始柱移動到目標柱的程序
else
{
hanio(n - 1, x, z, y); //將n-1個盤子從起始柱放到目標柱(第一次A->B,第二次B->A,后面往復)
printf("%c->%c\n", x, z); //列印初始柱移動到目標柱的程序
hanio(n - 1, y, x, z); //將n-1個盤子從起始柱放到目標柱(第一次B->C,第二次C->B,后面往復)
}
}
int main()
{
int n;
scanf("%d", &n);
hanio(n,'A','B','C');
return 0;
}
結語
- 以上青蛙跳臺階和漢諾塔問題主要是圍繞講解遞回思想,如果不太理解遞回,可以瀏覽上一章第六章 C世界函式的故事(2)進行查閱,
- 本次講解,不乏其中有很多漏洞和不足,如果大家覺得哪里不好,可以評論給我指正,希望和大家一起進步,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/280242.html
標籤:其他
上一篇:大資料在保險應用場景
下一篇:java仿QQ微信聊天室

