遞迴思想以及例項練習
思想分析:
1.對問題的細化成小問題。
2.自己呼叫比自己小一個規模的自己。
3.有結束條件。
滿足條件:
1.有遞迴公式。問題能夠分解為一個一個於自身類似的小問題。
2.有確切的邊界。能夠最後分解為一個有確定解的問題。
一、一個人趕著鴨子去每個村莊賣,每經過一個村子賣去所趕鴨子的一半又一隻。這樣他經過了七個村子後還剩兩隻鴨子,問他出發時共趕多少隻鴨子?經過每個村子賣出多少隻鴨子
分析:經過7個村莊後還剩兩隻雞鴨子,每經過一個村莊賣當前鴨子的一半加一隻,所以遞迴體是經過前一個村子的鴨子數除以2再減去1就是到達下一個村子的時候的鴨子數,所以要想求在第一個村子的鴨子數,必須從後往前推。
演算法: 當i=0時,結束遞迴呼叫,當i>0時,故有:
If(i=0)
Cout<< “共有x只鴨子”;
Else
Returnf(x+1);
程式碼實現:
#include <stdio.h> #include "iostream" using namespace std; int number = 2,x,i=7; int Number(int i) { if(0 == i) //當i為0時,結束遞迴呼叫 { cout << "他出發時共趕了" << number << "只鴨子。" << endl; //當經過的村子數為0時,number為出發時的鴨子數 } else { number = (number + 1 ) * 2; //計算在經過第i個村子前的鴨子數number x = number / 2 + 1; //計算在經過第i 個村子時賣的鴨子數x cout << "經過第" << i << "個村子時,他賣出" << x << "只鴨子。" << endl; return Number(i - 1); } } int main() { Number(7); }
二、角谷定理。輸入一個自然數,若為偶數,則把它除以2,若為奇數,則把它乘以3加1。經過如此有限次運算後,總可以得到自然數值1。求經過多少次可得到自然數1。
如:輸入22,
輸出 2211 34 17 52 26 13 40 20 10 5 16 8 4 2 1
STEP=16
分析:設fun(n)表示關於自然數n的一個函式,由題意已知,當n=1時,fun(1)=1。
當n>1且n為偶數時,fun(n)=fun(n/2);
當 n>1且n為奇數時,fun(n)=fun(3*n+1)。
最後得到自然數1的運算次數為每次運算次數之和。
演算法:fun(n)=1 n=1
fun(n)=fun(n/2) n>1且n為偶數
fun(n)=fun(3*n+1) n>1且n為奇數
程式碼實現:
#include<iostream>
using namespace std;
int Angle(int n,int i)
{
if(n==1) //當n 為1時,退出遞迴
{
cout<<endl<<"STEP="<<i<<endl; //輸出次數
return 1;
}
else
{
if(n%2==0) //當n為偶數時
{
cout<<n/2<<" ";
i=i+1; //次數加1
return Angle(n/2,i);
}
else //當n為奇數時
{
cout<<3*n+1<<" ";
i=i+1;
return Angle(3*n+1,i);
}
cout<<endl<<"STEP="<<i<<endl; //輸出次數
}
}
void main()
{
int m,j=1;//m為自然數,j為執行次數
scanf("%d",&m);
Angle(m,j);
system("pause");
return;
}
三、電話號碼對應的字元組合:在電話或者手機上,一個數字如2對應著字母ABC,7對應著PQRS。那麼數字串27所對應的字元的可能組合就有3*4=12種(如AP,BR等)。現在輸入一個3到11位長的電話號碼,請打印出這個電話號碼所對應的字元的所有可能組合和組合數。
分析:0到9所代表的字元0:"" 1:"" 2:ABC 3:DEF 4:GHI 5:JKL 6:MNO 7:PQRS 8:TUV 9:WXYZ
演算法:例如:23
1.show(0,2)-> output[0]=A-> show(1,2)-> 1!=2-> output[1]=D->
2.k=1 show(2,2)-> 2=len-> 輸出AD-> i+1-> output[1]=E-> show(2,2)-> 2=len-> 輸出AE i=2-> output[1]=F-> show(2,2)-> 2=len-> 輸出AF
3.再回到k=0-> i=1 output[0]=ch[0][1]=B 輸出BD,BE,BF i=2 輸出CD,CE,CF
程式碼實現:
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const char ch[10][10]={ //用陣列存放0到9所表示的字元
"",//0
"",//1
"ABC",//2
"DEF",//3
"GHI",//4
"JKL",//5
"MNO",//6
"PQRS",//7
"TUV",//8
"WXYZ"//9
};
const int total[10]={0,0,3,3,3,3,3,4,3,4}; //各個數字代表的字元總數構成的陣列
char input[20]; //儲存輸入電話字元陣列
char output[20]; //輸出可能字元組合
void show(int k,int len){
if(k==len){
output[len]='\0'; //設為結束標誌,輸出
printf("%s\n",output);
return ;
}
for(int i=0;i<total[input[k]];i++){
output[k]=ch[input[k]][i];
show(k+1,len);
}
}
int main(){
printf("請輸入電話號碼:\n");
scanf("%s",input); //輸入電話字元陣列
int len=strlen(input);
int number[20];
int sum=1;
for(int i=0;i<len;i++){
input[i]-='0'; //char型別轉換成int陣列
sum*=total[input[i]];
}
show(0,len);
printf("總的組合數:%d\n",sum);
return 0;
}
四、日本著名數學遊戲專家中村義作教授提出這樣一個問題:父親將2520個桔子分給六個兒子。分完 後父親說:“老大將分給你的桔子的1/8給老二;老二拿到後連同原先的桔子分1/7給老三;老三拿到後連同原先的桔子分1/6給老四;老四拿到後連同原先的桔子分1/5給老五;老五拿到後連同原先的桔子分1/4給老六;老六拿到後連同原先的桔子分1/3給老大”。結果大家手中的桔子正好一樣多。問六兄弟原來手中各有多少桔子?
分析:每個兒子的橘子數目有兩種,原有的和現有的,除了老大,其他的兒子原有的+別人給的-給別人的=現有的=平均數,老大是先分給其他人在得到,其他人都是得到再分出去。
演算法:If(i=7)
Return0;
Elseif(i = 1)
x = (420 - y) / 7 * 8;
y=x/8;
return f(i+1);
Elseif(i>1)
x = 420 * (9 - i) / (8 - i) - y;
y = (x + y) / (9 - i);
return f(i+1);
程式碼實現:
#include <stdio.h>
#include "iostream"
using namespace std;
//x表示每個人手裡的橘子數,y表示從別人那得到的橘子數 ,老六分給老大210個橘子
int x ,y = 210;
int Number(int i)
{
if(7 == i)
{
}
else if(1 == i) //老大是先分出去再得到
{
x = (420 - y) / 7 * 8; //420為老大最後手裡的橘子數
cout << "老大原本有" << x << "個橘子。" << endl;
y = x / 8; //計算老大分出去的橘子數
return Number(i + 1);
}
else if(i > 1) //其他人都是先得到再分出去
{
x = 420 * (9 - i) / (8 - i) - y; //計算手裡原本有多少橘子
cout << "老" << i << "原本有" <<x << "個橘子。"<< endl;;
y = (x + y) / (9 - i); // 計算得到橘子後再分出去的橘子數
return Number(i + 1);
}
}
void main()
{
Number(1);
}