1. 一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。这样他经过了七个村子后还剩两只鸭子,问他出发时总共赶了多少只鸭子?经过每个村子卖出多少只鸭子?
题目分析:经过7个村子后他还剩2只鸭子,而每经过一个村子卖出所赶鸭子的一半多一只,所以在经过第七个村子前他所赶的鸭子数为(2+1)*2=6只,经过第7个村子时卖出6/2+1=4只。设经过7个村子之后,即在经过第8个村子之前,village=7,duck=2,即f(7)=2。f(6)=(f(7)+1)*2,卖出f(6)/2+1只。以此类推,可得出他出发时的鸭子数以及经过每个村子卖出鸭子数。
算法分析:数学公式
f(x)表示经过x村子后剩余的鸭子数,出发前的总数count=((f(1)+1)*2)。
算法实现:
for (village = 0; village<7; village++)//经过7个村庄
count = (duck + 1) * 2;//每经过一个村庄之前剩余的鸭子数量
duck = count;//将值传给dcuck,获得最终鸭子数,实现递归
当village=7时是递归出口。
源代码:
/*
File Name:duck.cpp
Author:
Date:2018/11/16
IDE:Visual Studio 2013
*/
#include<iostream>
using namespace std;
int main()
{
int village;//村庄
int duck = 2;//经过7个村子之后的鸭子剩余数
int count = 0;//鸭子总数
cout << "经过第7个村庄后,鸭子剩余" << duck << "只" << endl;
for (village = 0; village<7; village++)//经过7个村庄
{
count = (duck + 1) * 2;//每经过一个村庄之前剩余的鸭子数量
duck = count;//将值传给duck,获得最终鸭子数,实现递归
/*剩余的鸭子数+1才是卖出之前总鸭子数的一半,例如:
经过第7个村庄后剩余2只鸭子,那么经过第7个村庄之前的总鸭子数为:duck=(2+1)*2
在第7个村庄卖出count/2+1只鸭子,剩余duck-((count+1)/2+1)只
*/
cout << "经过第" << 7 - village << "个村庄时卖出" << (count + 1) / 2 + 1 << "只,"
<< "剩余" << duck - ((count + 1) / 2 + 1) << "只" << endl;
}
cout << "出发前的鸭子总数为" << count << "只" << endl;
//返回鸭子总数
return count;
}
运行结果:
2.角谷定理。输入一个自然数,若为偶数,则把它除以2,若为奇数,则把它乘以3加1。经过如此有限次运算后,总可以得到自然数值1。求经过多少次可得到自然数1。如:输入22,输出 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1,STEP=16。
问题分析:递归的终止条件是最后值为1;先输入的数n 进行判断,若 n = 1,则输出1,STEP=1;若输入的数为偶数, 则把它除以2,STEP+=1,接着再判断n的值;若为奇数则乘3加1,STEP+=1,接着继续判断;直到n = 1时停止。
算法分析:
n为输入的自然数,step为步数。
算法实现:
while (1)
{
if (n % 2 == 0) //如果n是偶数
{
n = n / 2; //将n除以2之后将值再传给n,再接着判断是偶数还是奇数
cout << n * 2 << "/2=" << n << endl;//输出偶数算式
step += 1;//记录n为偶数时循环次数
/*递归出口,当n=1时输出需要的步数,退出递归*/
if (n == 1)
{
cout <<"共需要的步数(输入的自然数算第一步):STEP="<< step << endl;//输出需要的步数
break;
}
}
else //如果n是奇数
{
n = n * 3 + 1;//将n*3+1再传给n,接着再判断是奇数还是偶数
cout << (n - 1) / 3 << "*3+1=" << n << endl;//输出奇数算式
step += 1;//记录n为奇数的循环次数
}
}
源代码:
/*
File Name:jiaogu.cpp
Author:
Date:2018/11/16
IDE:Visual Studio 2013
问题描述:
输入一个自然数,若为偶数,则把它除以2,
若为奇数,则把它乘以3加1。
经过如此有限次运算后,总可以得到自然数值1。
求经过多少次可得到自然数1。
*/
#include<iostream>
using namespace std;
/*递归函数,传入自然数和步数*/
int JiaoGu(int n, int step)
{
/*循环体,实现递归操作,直到自然数n=1时退出*/
while (1)
{
if (n % 2 == 0) //如果n是偶数
{
n = n / 2; //将n除以2之后将值再传给n,再接着判断是偶数还是奇数
cout << n * 2 << "/2=" << n << endl;//输出偶数算式
step += 1;//记录n为偶数时循环次数
/*递归出口,当n=1时输出需要的步数,退出递归*/
if (n == 1)
{
cout <<"共需要的步数(输入的自然数算第一步):STEP="<< step << endl;//输出需要的步数
break;
}
}
else //如果n是奇数
{
n = n * 3 + 1;//将n*3+1再传给n,接着再判断是奇数还是偶数
cout << (n - 1) / 3 << "*3+1=" << n << endl;//输出奇数算式
step += 1;//记录n为奇数的循环次数
}
}
return n;//返回自然数
}
void main()//主函数
{
int n;//自然数
int step=1;//计算需要的步数,从输入的数开始就算第一步
cout << "请输入一个自然数:" << endl;
cin >> n;//从键盘输入一个自然数
cout << "过程如下所示:" << endl;
JiaoGu(n, step);//调用递归函数,传入一个自然数与初始步数
}
运行结果:
3. 电话号码对应的字符组合:在电话或者手机上,一个数字如2对应着字母ABC,7对应着PQRS。那么数字串27所对应的字符的可能组合就有3*4=12种(如AP,BR等)。现在输入一个3到11位长的电话号码,请打印出这个电话号码所对应的字符的所有可能组合和组合数。
题目分析:由题目可知,数字所对应的字符串为:0—“”,1—“”,2—“ABC”,3—“DEF”,4—“GHI”,5—“JKL”,6—“MNO”,7-“PQRS”,8—“TUV”,9—“WXYZ”,与手机键盘对应。数字与字符数对应关系:0—0,1—0,2—3,3—3,4—3,5—3,6—3,7—4,8—3,9—4。
算法分析:递归出口是:当输入的是一个数字时,返回这个数字所有可能性的组合,递归体是当输入一串数字,每个数字代表不同的字符串,返回最后一个数字跟前面已产生的字符串进行组合。如输入567,则有3*3*4=36种组合,每一个组合必定包含三个字符串,如{“JMP”,“JNP”….}等。
算法实现:
if (p == len)//如果数组遍历至最后一位,退出递归
{
cout << output << endl;//输出所有字符串组合
}
for (int i = 0; i<number[input[p]]; i++)//遍历数组number
{
output[p] = ph[input[p]][i];//将所有可能的组合情况保存至output数组中
Phone(p + 1, len);//继续递归
}
源代码:
/*
File Name:phone.cpp
Author:
Date:2018/11/16
IDE:Visual Studio 2013
*/
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//定义保存对应字符串的指针数组
char* ph[10] = {"", "", "ABC", "DEF", "GHI",
"JKL", "MNO", "PQRS", "TUVW", "XYZ"};
int number[10] = { 0, 0, 3, 3, 3, 3, 3, 4, 3, 4 };//字符串个数数组
char input[50];//input数组保存从键盘输入的数字
char output[50];//output数组保存输出的字符串组合
/*定义递归函数,遍历数组*/
void Phone(int p, int len) //p表示数组下标,len表示数组长度
{
if (p == len)//如果数组遍历至最后一位,退出递归
{
cout << output << endl;//输出所有字符串组合
}
for (int i = 0; i<number[input[p]]; i++)//遍历数组number
{
output[p] = ph[input[p]][i];//将所有可能的组合情况保存至output数组中
Phone(p + 1, len);//继续递归
}
}
int main()
{
int len=0;//定义数组长度变量,初始化为0
cout << "请输入一个3到11位长的电话号码:" << endl;
cin >> input;//将键盘输入保存至input数组中
cout<<"该电话号码所对应字符的所有可能的组合是:"<<endl;
len = strlen(input);//strlen()函数获取数组长度,并将长度值赋给len,
int count = 1;//所有可能组合总数
for (int i = 0; i<len; i++)//遍历input数组
{
input[i] -= '0';//转换为字符
//计算总数,例如:输入789,则寻找5,6,7所对应的number数组中的值,即3*4*4=48
count *= number[input[i]];
}
Phone(0, len);//调用递归函数,数组从第一位开始遍历
cout << "组合数:" << count << endl;
return 0;
}
运行结果:
4. 日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完 后父亲说:“老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子?
题目分析:由题可知,分完之后6兄弟手中的橘子一样多,即每个人手中的句子为avg=2520/6=420;因为老六分出1/3给老大之后手中剩余420个橘子,所以老六之前总的橘子数为:420/(3/2)=630个,所以他分给老大的橘子数为630*(1/3)=210个,设老大原来手中剩余的橘子数为x,那么分完之后他的橘子数为x+210=420个,所以他手中原来有x=210只。由于他需要分给老二1/8,所以210只是他分完之后剩余的,所以他原来拥有的橘子数为210*(8/7)=240只橘子。以此类推即可算出其他人原来手中有的橘子数。
算法分析:
f(x)为原来手中的橘子数,divide为分出去的橘子数,i表示儿子,从1-6。6个儿子。递归出口为i=7。即只计算到第6个儿子,到第7个时结束。
算法实现:i
f (i == 0)//如果是第一个儿子
{
num = avg / 2 * (8 - i) / (8 - i - 1);
divide = num / (8-i);
return cal(i + 1);//返回cal函数下一个儿子的橘子数计算方法
}
else if (i > 0 && i < 6) //从第二个儿子开始
{
num = avg*(8 - i) / (8 - i - 1) - divide;//原本拥有的橘子数=总数-前一个儿子分给他的数量
/*从第二个儿子开始每次分给下一个人的橘子数divide=原本橘子数+上一个儿子分过来的橘子数*/
divide = (num+divide) / (8 - i );
return cal(i + 1);//递归
}
else
//如果i=6,即超过6个数,则退出递归
return;
源代码:
/*
File Name:Orange.cpp
Author:
Date:2018/11/16
IDE:Visual Studio 2013
*/
#include<iostream>
using namespace std;
int num;//表示每个人原来手里的橘子数
int avg = 420;//分完的结果为等值240
int divide;//分出去的橘子数,即下一个儿子得到的橘子数
/*递归函数,计算原本橘子数和分出的橘子数,返回下一个儿子,当i=6时退出递归*/
void cal(int i)
{
if (i == 0)//如果是第一个儿子
{
num = avg / 2 * (8 - i) / (8 - i - 1);
divide = num / (8-i);
cout << "老" << i + 1 << "原本拥有的橘子数:" << num << "个,"
<< "分出去的橘子数:(即下一个儿子得到的橘子数)" << divide << "个" << endl;
return cal(i + 1);//返回cal函数下一个儿子的橘子数计算方法
}
else if (i > 0 && i < 6) //从第二个儿子开始
{
num = avg*(8 - i) / (8 - i - 1) - divide;//原本拥有的橘子数=总数-前一个儿子分给他的数量
/*从第二个儿子开始每次分给下一个人的橘子数divide=原本橘子数+上一个儿子分过来的橘子数*/
divide = (num+divide) / (8 - i);
cout << "老" << i + 1 << "原本拥有的橘子数:" << num << "个,"
<< "分出去的橘子数:(即下一个儿子得到的橘子数)" << divide << "个" << endl;
return cal(i + 1);//递归
}
else
//如果i=6,即超过6个数,则退出递归
return;
}
/*主函数*/
int main (){
cal(0);//调用递归函数,从第一个儿子开始
}
运行结果: