C++ 基础算法 - 递推 & 递归
今天,我们来学习第三和第四个算法递推&递归
递推是什么
递推是利用已知的条件和特定的关系从而一步步推导直至获得答案的算法,这种算法与递归是差不多的,只不过递归是利用函数的栈进行的,递推则不需要
递推分为两种,一种是顺推,另一种则是逆推
顺推就是从已知的条件逐步得到结果
逆推就是从问题出发推向已知条件
由此可见,递推最重要的就是学会利用已知条件和关系
递推
递推没有什么确切的结构,所以直接先看一道题目
x[]={1,-2,4,-7,11,-16,22,-29,37,-46,56,...};首先看看有什么已知的条件,就是这一条数列提示的数
然后再看看有什么关系,不难发现第一个关系,数组的第 0 位是正数,而第 1 位是负数,第 2 位又是正数,以此类推就知道
第偶数位为正数,第奇数位为负数
然后,不难发现第二个关系
在不考虑符号的情况下,第 n 个数的值是第 n-1 个数的值加上 n-1
有了这两个关系作为基础,再加上已知的条件,我们就可以进行递推了
int i,m=1;//应该初始化为1 for(i=2;i<=a;++i)//循环直到a循环到 a 就可以停下来了,随后我们就要用到两个关系了
m=abs(m)+i-1;//abs(m)是上一位的值的绝对值,i-1是上一位的位数
if(i%2==0) m=-m;//如果是偶数位就变为负数
这里之所以要用到 abs 函数,是因为第二条关系是在不考虑符号的情况下才会成立的,所以我们就要用绝对值函数来取正数了
完整代码
#include<iostream>
#include<cstdio>
#include<c**th>//abs函数需要c**th头文件
using namespace std;
int **in()
{
int i,m=1,a;
scanf("%d",&a);//输入
for(i=2;i<=a;++i)//核心代码
{
m=abs(m)+i-1;
if(i%2==0) m=-m;
}
printf("%d\n",m);//输出
return 0;
}
从这道题目可以看出,递推最重要的就是找到它的关系
递归
递归与递推是非常像的,它们都是将大问题分解成为小问题,较后逐步解开,但是递归和递推的分解方式不同,递归是用函数重复调用自身,以达到分解问题,并将返回每一部分的答案综合起来,最终达到答案
递归最核心的部分就是函数直接或间接地进行自我调用,分解问题,这样的方法让人更容易理解,也就会更加方便
int Fibonacii(int n);例如现在要求斐波那契数列的第 n 项,首先,程序执行进入函数
if(n==1) return 1;
if(n==2) return 1;
先通过已知的条件,也就是斐波那契数列的第 1 位和第 2 位都为 1,也就是说,当程序要求第 1 位或者第 2 位时,这是已知的,可以直接使用
return Fibonacii(n-1)+Fibonacii(n-2);当然可能有一些人不太理解为什么能够这样做
首先,函数自己调用自己时,我们可以想象成调用自己的本体叫做 Fibonacii_1,被调用的函数叫做 Fibonacii_2,这样的话 Fibonacii_1 和 Fibonacii_2 内部的变量虽然有着相同的名称,但是它们并不会互相影响
这时,Fibonacii_2 所返回的数就会给到 Fibonacii_1 中,供 Fibonacii_1 所使用,通过这样反复地调用自身,就会有很多个 Fibonacii 函数,但是它们之间都有着明确的关系,相应的函数返回到相应的函数当中去,大问题就被分解了
在利用递归求解时,一定会有的就是固定的返回值,这就是问题的**小单位,所有问题都能够被分解成这样的小问题,这就是递归的出口,没有出口的递归,较后都只能是卡死或运行错误,因为程序执行如果没有办法退出,就没有办法真正地找到结果,所以,一个真正的递归必须要有退出的方法
其实递归在执行的过程中,程序会给 Fibonacii_n 这个函数分配一个栈,通过栈的层层叠加和返回从而求出答案
递归和递推的优缺点
递归和递推相比,递归更容易理解且简单好用,但是上面说了,每个 Fibonacii_n 都会要一个栈空间,可是栈不是无限大的,它是有限的,所以如果栈空间用完了,程序也就不能继续递归了,但是递归在完成返回值之后就会将这个 Fibonacii_n 所占用的空间释放,不会一直占用
递推虽然较递归难理解一些,实现也较难,但是它使用的是循环,多用亿点也没问题...
逻辑运算
逻辑运算是对真或假进行操作的运算,但是它也是非常重要的,在代码中可以利用逻辑运算进行判断,所以在这里也是要讲一讲 ( 要不然有什么理由拖更那么久 )
逻辑运算符 :
这三种运算就是逻辑运算中最基本的运算
逻辑运算也有级别,要先算括号内的,然后是逻辑非运算,再然后是逻辑与运算,较后是逻辑或运算
你知道吗 ( 特别栏目 )
**in 函数也可以进行递归 !
一本通平台讲解 1312 - 1316,1188 - 1211
1312:【例3.4】昆虫繁殖
这道题先找关系,题中有两个关系
第 i 个月新增虫卵的数量 = x 个月前成虫的数量 × 每过 x 个月产出 y 对卵
第 i 个月的成虫数量 = 第 i - 1 个月的成虫数量 + 第 i - 2 个月新增的虫卵数量
由这两个关系就可以进行递推了,代码如下
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long a[101],b[101];
int **in()
{
long long x,y,z,i;
scanf("%lld%lld%lld",&x,&y,&z);
for(i=1;i<=x;i++) a=1;//初始化成虫的数量
for(i=x+1;i<=z+1;i++)//因为是第z个月后,所以需要加1
{
b=y*a[i-x];
a=a[i-1]+b[i-2];//根据关系进行推导
}
printf("%lld\n",a[z+1]);
return 0;
}
1313:【例3.5】位数问题
同样先找递推的关系
当 N = 1 时,符合条件的有 1 , 2 , 4 , 5 , 6 , 7 , 8 , 9 这个 8 个数
当 N = 2 是,符合条件的有以下几种
含有 2 个 3 的数,即 33,共 1 个
含有 0 个 3 的数,即除了 30 ~ 39 ( 10 个十位上为 3 的,包括 33 这个特殊的 ) 和 13 , 23 , ... , 93 ( 9 个个位上为 3 的 ),因为共有 99 - 10 + 1 = 90 个数,用 90 - 10 - 9 + 1( 重复的 33 ) = 72 个,共 72 个
符合条件的有 72 + 1 = 73 个数
当 N = 3 时,符合条件的有 226 个 ( 这里就不给出计算方法了 )
因为符合条件的在本次放置 3 中就变成不符合的,反之亦然
代码如下
#include<iostream>
#include<cstdio>
using namespace std;
long long a[1100],b[1100];
int **in()
{
int i,n,x;
scanf("%d",&n);
if(n<=1)//特判
{
printf("9\n");
return 0;
}
a[1]=8;
b[1]=1;
for(i=2;i<=n;i++)
{
a=(a[i-1]*9+b[i-1])%12345;//每一次都要%12345,这样才能较大限度的保证
b=(b[i-1]*9+a[i-1])%12345;//数据不超出范围
}
printf("%lld\n",a[n]);
return 0;
}
1314:【例3.6】过河卒(Noip2002)
这道题先来思考关系,移动时,如果一个地方是可以到达的,那么它到达它所有的方案是从上方到自己或是从左方到自己,那么到达自身的方案数 = 从上方到自身的方案数 + 从左方到自身的方案数
但是,这又引出了一个新的问题,如果这个点是在上边缘或左边缘的话,它就没有上面的点到达自身的方案数或从左面的点到达自身的方案数,简单点说就是缺少了成分
可是再想想就能够发现,上边缘和左边缘的方案数都只有 1 种,这意味着可以对它进行初始的赋值,也就解决了上面的问题,较后按照关系式,逐步推出较后点的方案数即可
代码如下
#include<iostream>
#include<cstdio>
using namespace std;
long long a[50][50],Map[50][50];
int **in()
{
int n,m,cx,cy,i,j;
scanf("%d%d%d%d",&n,&m,&cx,&cy);
a[0][0]=1;
Map[cx][cy]=1;
if(cx-2>=0&&cy-1>=0) Map[cx-2][cy-1]=1;
if(cx-1>=0&&cy-2>=0) Map[cx-1][cy-2]=1;
if(cx+1<=n&&cy-2>=0) Map[cx+1][cy-2]=1;
if(cx+2<=n&&cy-1>=0) Map[cx+2][cy-1]=1;
if(cx+2<=n&&cy+1<=m) Map[cx+2][cy+1]=1;
if(cx+1<=n&&cy+2<=m) Map[cx+1][cy+2]=1;
if(cx-1>=0&&cy+2<=m) Map[cx-1][cy+2]=1;
if(cx-2>=0&&cy+1<=m) Map[cx-2][cy+1]=1;//将所有不能走的地方标记为1
for(i=1;i<=n;i++) if(Map[0]==0) a[0]=a[i-1][0];//初始化边缘
for(i=1;i<=m;i++) if(Map[0]==0) a[0]=a[0][i-1];
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(Map[j]==1) a[j]=0;//不能走的地方改为0
if(Map[j]==0) a[j]=a[i-1][j]+a[j-1];//根据关系公式计算
}
}
printf("%lld\n",a[n][m]);//输出
return 0;
}
1315:【例4.5】**的划分
这道题乍一看有很多符号都没见过,那么,首先要知道什么是** ( 当然如果已经懂了就可以直接跳过这一部分 )
**是由一个或多个确定的元素所构成的整体
其中
**一般这样表示
A={1,2,3,4,5}
B={3,4,5,6,7}
C={0,2,4,6,8}
D={10}
E={1}
这样看是不是很像数组?那么
那么题目中的条件就可以这样翻译 :
下面也给出了提示,即相当于将** S 内的 n 个元素全部放入 k 个无标号的盒子内,使得没有一个盒子为空
接下来看看应该怎么做,还是先找关系,这道题要求找到所有符合条件的划分方法,可以这样做,按照顺序将 S **内的元素放入盒子中,直到盒子放满了就将划分数增加
题目给出了有 n 个元素,并且有 k 个盒子,那么在放元素时就会分成多个情况
k > n 意思就是盒子的数量大于元素的数量这样无论如何装,盒子总会有空的,所以遇到这种情况就直接不再继续查找
k = n 意思就是盒子的数量和元素的数量相同,每个盒子里都必须装一个元素,这是唯一的一种方法
k = 1 意思就是只有一个盒子,所以只能将剩下的所有元素都放入到较后一个盒子里
如果 k 和 n 的值都不满足以上的条件,就要进行分类讨论 : (1) 可以将此元素放入一个新的盒子中,将其它的元素放在 k - 1 个盒子中 ( 留给下一次操作来做 );(2) 先不考虑此元素放在哪里,先放剩下的 n - 1 个元素,较后再将此元素放在由其它元素所组成的所有盒子中的任意一个,较后就会有 n - 1 个元素的分装方法 × k ,即盒子总数,因为由其它元素所分装的方法各有不同,加上本元素的 k 个位置的方法后就会有更多的方法
那么,下面就是代码
#include<iostream>
#include<cstdio>
using namespace std;
long long Subcollection(long long n,long long k)
{
if(k>n) return 0;//如果盒子不够
if(n==k||k==1) return 1;//如果盒子刚好够或者只有一个盒子
return Subcollection(n-1,k-1)+Subcollection(n-1,k)*k;
//这里分为两部分,前面是将新元素放在新盒子里面,所以元素和盒子的数量都要减1
//后面是将新元素搁置,先找出后n-1个元素的排列总数,较后再×k
}
int **in()
{
long long n,k;
scanf("%lld%lld",&n,&k);//输入
printf("%lld\n",Subcollection(n,k));//输出
return 0;
}
1316:【例4.6】数的计数(Noip2001)
#include<iostream>
#include<cstdio>
using namespace std;
long long x[100000],y[100000];
int **in()
{
long long i,j,n;
scanf("%lld",&n);
for(i=1;i<=n;++i)
{
x=1;//需要加1,直接初始化为1即可
for(j=1;j<=i/2;++j) x+=x[j];//按照关系进行递推
}
printf("%lld\n",x[n]);
return 0;
}
当然这道题还可以优化
#include<iostream>
#include<cstdio>
using namespace std;
long long x[100000],y[100000];
int **in()
{
long long i,j,n;
scanf("%lld",&n);
for(i=1;i<=n;++i)
{
x=1+y[i/2];//通过之前的答案来计算新的值
y=y[i-1]+x;//将需要重复利用的答案存储起来,下次就不用重新计算了
}
printf("%lld\n",x[n]);
return 0;
}
1188:菲波那契数列(2)
这道题很简单,就直接跳过了
#include<iostream>
#include<cstdio>
using namespace std;
int a[1000001];
int **in()
{
int n,x,i;
a[1]=1;
a[2]=1;
for(i=3;i<=1000000;i++) a=(a[i-1]+a[i-2])%1000;//提前计算好1000000以内的数
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);//输入
printf("%d\n",a[x]);//输出
}
return 0;
}
1189:Pell数列
这道题的题目已经给出了关系,只需按照关系进行递推即可
#include<iostream>
using namespace std;
int a[1000001];
int **in()
{
int n,x,i;
a[1]=1;
a[2]=2;
for(i=3;i<=1000000;i++) a=(2*a[i-1]+a[i-2])%32767;//按照关系先进行计算
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
printf("%d\n",a[x]);//输出
}
return 0;
}
其实只需要从上一题改一下就行了,所以只要能理解上一题就一定能理解这一题
1190:上台阶
先思考关系,上到一层台阶可以怎么上
从下一层上来
从下两层上来
从下三层上来
那么,第 N 级台阶方案数 = N - 1 级台阶的方案数 + N - 2 级台阶方案数 + N - 3 级台阶方案数
#include<iostream>
#include<cstdio>
using namespace std;
long long a[110];
int **in()
{
int i,n;
a[1]=1;a[2]=2;a[3]=4;
for(i=4;i<=73;++i) a=a[i-1]+a[i-2]+a[i-3];//根据关系计算
while(true)
{
scanf("%d",&n);
if(n==0) return 0;//判断是否结束
else printf("%lld\n",a[n]);//输出
}
}
1191:流感传染
模拟传染情况即可
#include<iostream>
#include<cstdio>
using namespace std;
char a[101][101];
bool b[101][101];
int **in()
{
int i,j,k,n,m,sum=0;
scanf("%d",&n);
for(i=1;i<=n;++i) for(j=1;j<=n;++j) cin>>a[j];
scanf("%d",&m);
for(k=2;k<=m;++k)
{
for(i=1;i<=n;++i)
{
for(j=1;j<=n;++j)
{
if(a[j]=='@')
{
if(a[i+1][j]=='.') b[i+1][j]=true;
if(a[i-1][j]=='.') b[i-1][j]=true;
if(a[j+1]=='.') b[j+1]=true;
if(a[j-1]=='.') b[j-1]=true;
}
}
}
for(i=1;i<=n;++i) for(j=1;j<=n;++j) if(b[j]) a[j]='@';
}
for(i=1;i<=n;++i) for(j=1;j<=n;++j) if(a[j]=='@') sum++;
printf("%d\n",sum);
return 0;
}
1192 ~ 1193:放苹果 & 吃糖果
咳咳,相信你们也打不开吧,打得开肯定会做吧 ? 所以就没有啦...
1194:移动路线
这道题和 1314:【例3.6】过河卒(Noip2002) 类似
#include<iostream>
#include<cstdio>
using namespace std;
long long Map[30][30];
int **in()
{
int i,j,n,m;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i) Map[1]=1;
for(i=1;i<=m;++i) Map[1]=1;
for(i=2;i<=n;++i) for(j=2;j<=m;++j) Map[j]=Map[i-1][j]+Map[j-1];
printf("%lld",Map[n][m]);
return 0;
}
1195:判断整除
先看代码 ( f[j] 代表前 i 个数是否能够被 j 整除 )#include<iostream>
#include<cstdio>
using namespace std;
bool f[10005][105];
int a[10005];
int **in()
{
int i,j,n,k;
scanf("%d%d",&n,&k);
for(i=1;i<=n;++i) scanf("%d",&a);
f[0][0]=true;
for(i=1;i<=n;++i)
{
for(j=0;j<k;++j)
{
if(f[i-1][(j-a%k+k)%k]||f[i-1][(j+a%k+k)%k]) f[j]=true;
else f[j]=false;
}
}
if(f[n][0]) printf("YES\n");
else printf("NO\n");
return 0;
}1196:踩方格
这道题在不同矩阵中的结果是有一定的关系的
#include<iostream>
#include<cstdio>
using namespace std;
int x[30];
int **in()
{
int i,n;
scanf("%d",&n);
x[1]=3;x[2]=7;
for(i=3;i<=n;i++) x=2*x[i-1]+x[i-2];
printf("%d\n",x[n]);
return 0;
}
1197:山区建小学
额,这道题并不是递推,而是动态规划 ( 怎么跑到这里来了额 )
动态规划 ( Dynamic Programming,简称 DP ),跟递推差不多,但是要更加复杂一些
所以这里就先不讲动态规划了
#include<iostream>
#include<cstdio>
using namespace std;
int a[1000][1000],b[1000][1000],f[1000][1000];
int **in()
{
int i,j,k,n,m,mid;
scanf("%d%d",&m,&n);
for(i=1;i<m;i++) scanf("%d",&a[i+1]);
for(i=1;i<=m;i++) for(j=i+1;j<=m;j++) a[j]=a[j]=a[j-1]+a[j-1][j];
for(i=1;i<=m;i++)
{
for(j=i+1;j<=m;j++)
{
mid=(i+j)/2;
for(k=i;k<=j;k++) b[j]+=a[k][mid];
}
}
for(i=1;i<=m;i++) f[1]=b[1];
for(i=1;i<=m;i++)
{
for(j=2;j<=n;j++)
{
f[j]=999999;
for(k=j-1;k<=i;k++) f[j]=min(f[j],f[k][j-1]+b[k+1]);
}
}
printf("%d\n",f[m][n]);
return 0;
}
1198:逆波兰表达式
[ x * y + ( z - a ) / b ] % c % + * x y / - z a b c虽然逆波兰表达式将符号提前了,但是仍然能够计算,因为对于一个逆波兰表达式,最先找到的就是一个式子的运算符号,运算符号是紧挨着两个算式的,意思就是如果发现了一个符号,接下来就会有两个算式,当输入完成两个算式后,就可以使用符号,一旦用完了符号,就说明算式结束了
代码如下
#include<iostream>
#include<cstdio>
#include<cstdlib>//atof()所需的头文件
using namespace std;
char a[55];
double f()
{
scanf("%s",&a);
if(a[0]=='+') return nbl()+nbl();
else if(a[0]=='-') return nbl()-nbl();
else if(a[0]=='*') return nbl()*nbl();
else if(a[0]=='/') return nbl()/nbl();//将符号应用于之后的算式中
else return atof(a);
//递归的出口,找到了数字就返回,atof()的作用是将char类型的数组转化为double类型
//例如"12345.6789"就会变成12345.6789
}
int **in()
{
printf("%f\n",f());
return 0;
}
递归相较于递推较好理解,只需按照递归的层次顺序去模拟过程就能够明白
1199:全排列
全排列比较难的地方在于需要标记某个数字选没选过,方便下一次的选取
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
bool b[27];
string s;
int l;
void search(int i,string x)
{
for(int j=0;j<l;++j)
{
if(!b[s[j]-'a'])
{
b[s[j]-'a']=true;//将选过的字母标记为true
if(i==l-1) printf("%s%c\n",x.c_str(),s[j]);//输出
else search(i+1,x+s[j]);//位置增加,x+s[j]就是将字符串拼起来
b[s[j]-'a']=false;//用完了随手放回(标记为为选过,让后面能够选)
}
}
}
int **in()
{
getline(cin,s);//输入
l=s.size();//计算长度
search(0,"");//搜索
return 0;
}
1200:分解因数
这里的分解可以从**小 2 开始分解,值得注意的是每次的分解因数不能够小于上一次的分解因数
#include<iostream>
#include<cstdio>
using namespace std;
int sum;
void decomposition(int n,int last)//n为分解出来的数,last为上一次用于分解的除数
{
if(n==1)//如果完全分解了
{
sum++;//分解方案数增加
return;//返回上一层分解式
}
int i;
for(i=last;i<=n;++i) if(n%i==0) decomposition(n/i,i);
//循环从上一次的分解除数开始,结束于n,因为n除以大于n的数是不能分解的
//成功就可以进行下一层的分解式
}
int **in()
{
int n,x;
scanf("%d",&n);
while(n--)//这个也是同样可供输入n次的循环
{
scanf("%d",&x);//输入
decomposition(x,2);//进行分解
printf("%d\n",sum);
sum=0;
}
return 0;
}
1201:菲波那契数列
斐波那契数列的递归版本,在递归示范中就已经有了这里就不讲了
#include<iostream>
#include<cstdio>
using namespace std;
int Fibonacii(int x)
{
if(x==1||x==2) return 1;
return Fibonacii(x-1)+Fibonacii(x-2);
}
int **in()
{
int i,n,x;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
printf("%d\n",Fibonacii(x));
}
return 0;
}
1202:Pell数列
按照斐波那契数列的方法就能过做出来,在这里教大家一个方法去减少代码量
#include<iostream>
#include<cstdio>
#define M 32767//下面会讲
using namespace std;
int ans[1000000];
int Pell(int x)
{
if(ans[x]!=0) return ans[x];//记忆化搜索,下面会讲
if(x==1) return ans[x]=1;
if(x==2) return ans[x]=2;
return ans[x]=(2*Pell(x-1)%M+Pell(x-2)%M)%M;
//反复进行取模运算,防止爆空间
}
int **in()
{
int i,n,x;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
printf("%d\n",Pell(x));
}
return 0;
}
如何减少代码量
#define M 32767例如本题中将取模的常数定义为了 M,编译器就会将所有 M 替换成为 M 后面的东西,当然 M 是类似于变量的一种存在,不会将所有字符 M 都替换的
M 后面的东西不一定是数字,也可以是一些其它的东西
记忆化搜索
当一个递归或者搜索在进行时,会将问题分解成小问题进行计算,但是这些小问题可能会被重复计算过了,所以我们可以把答案放入一个答案数组当中去,要用的时候可以直接使用,不必重新计算,可以节约时间
1203:扩号匹配问题
还是比较简单的一题的,有不懂的看代码中的注释
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char a[101],b[101];
int up(int c)
{
if(c==-1) return c;//如果找出界了就表示不匹配
else if(b[c]=='$') return c;//找到了同样不匹配的右括号就返回其位置
else return up(c-1);//没找到就接着往前找
}
int **in()
{
int i,s,l;
while(scanf("%s",a)!=EOF)//读不到就退出
{
printf("%s\n",a);//先输出
l=strlen(a);
memset(b,' ',sizeof(b));//需要清空一下才行
for(i=0;i<l;i++)
{
if(a=='(') b='$';//先默认a是不匹配的左括号
else if(a==')')//如果找到右括号
{
s=up(i-1);//向前找
if(s==-1) b='?';//如果没找到就表示这个右括号是不匹配的
else b[s]=' ';//否则将匹配到的左括号的不匹配符给去掉
}
}
printf("%s\n",b);//输出
}
return 0;
}
1204:爬楼梯
这道题也是可以有记忆化搜索的,可以尝试做一做
#include<iostream>
#include<cstdio>
using namespace std;
int climb(int x)
{
if(x==1||x==2) return x;//即返回对应的
return climb(x-1)+climb(x-2);//进行查找
}
int **in()
{
int n;
while(scanf("%d",&n)!=EOF) printf("%d\n",climb(n));//读入
return 0;
}
1205:汉诺塔问题
这道题,可以将 c 看做是中转点,通过在 a 和 b 之间的转换就能够解出
#include<iostream>
#include<cstdio>
using namespace std;
void Hanoi(int n,char a,char c,char b)
{
if(n==1)
{
printf("%c->%d->%c\n",a,n,b);
return;
}
Hanoi(n-1,a,b,c);
printf("%c->%d->%c\n",a,n,b);
Hanoi(n-1,c,a,b);
}
int **in()
{
int n;
char a,b,c;
scanf("%d%c%c%c",&n,&a,&b,&c);
Hanoi(n,a,c,b);
return 0;
}
1206:放苹果
通过递归实现放苹果的方法数
#include<iostream>
#include<cstdio>
using namespace std;
int apple(int m,int n)
{
if(m==0||n==1) return 1;//没有苹果或只有1个盘子时分法为1种
if(m<n) return apple(m,m);
return apple(m,n-1)+apple(m-n,n);
}
int **in()
{
int i,t,m,n;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
scanf("%d%d",&m,&n);//输入
printf("%d\n",apple(m,n));//输出
}
return 0;
}
1207:求较大公约数问题
本题使用辗转相除法求较大公约数即可
#include<iostream>
#include<cstdio>
using namespace std;
int **(int a,int b)//辗转相除法
{
if(b==0) return a;
return **(b,a%b);
}
int **in()
{
int x,y;
scanf("%d%d",&x,&y);
if(x<y) swap(x,y);
printf("%d\n",**(x,y));
return 0;
}
1208:2的幂次方表示
整体结构类似 ( 也许吧? ) 于 1200:分解因数 的样子进行分解
#include<iostream>
#include<cstdio>
using namespace std;
string decomposition(int n)
{
if(n==0) return "0";
int i,t;
string ans;
for(i=0,t=1;t<n;i++) t*=2;//判断t的多少次方为n
i--;//多加了一次,要减去1
t/=2;//多乘了一次,要除以2
if(i==1) ans="2";//如果是1次方
else
{
ans="2(";//如果不是1次方,就继续将次方分解
ans+=decomposition(i);
ans+=")";
}
if(n-t)//如果单项式不够,就多几个式子
{
ans+="+";
ans+=decomposition(n-t);
}
return ans;
}
int **in()
{
int n;
scanf("%d",&n);
printf("%s\n",decomposition(n).c_str());
return 0;
}
1209:分数求和
利用辗转相除法求较大公约数进行通分求和
#include<iostream>
#include<cstdio>
int **(int a,int b)
{
if(a%b==0) return b;
else return **(b,a%b);
}
int **in()
{
int n,m,i,a,b,c,d;
scanf("%d%d/%d",&n,&a,&b);
for(i=2;i<=n;i++)
{
scanf("%d/%d",&c,&d);
m=b*d/**(b,d);
a*=m/b,b=m;
a+=c*(m/d);//通分并相加
}
m=**(a,b);
a/=m,b/=m;
if(b==1) printf("%d\n",a);//如果分母为1
else printf("%d/%d\n",a,b);//分母不为1
return 0;
}
1210:因子分解
同样是分解,只是复杂一些
#include<iostream>
#include<cstdio>
using namespace std;
int f=0;
void decomposition(int n,int x)
{
if(n==1) return;//分解完成
int y=0;
while(n%x==0)//如果能够进行分解
{
++y;
n/=x;//进行分解
}
if(y!=0)//如果成功分解过
{
if(f==1) printf("*");
if(y!=1) printf("%d^%d",x,y);
else printf("%d",x);
f=1;
}
decomposition(n,++x);
}
int **in()
{
int n;
scanf("%d",&n);
decomposition(n,2);
return 0;
}
1211:判断元素是否存在
这题也很简单,就不过多赘述了
#include<iostream>
#include<cstdio>
using namespace std;
bool flag;
bool search(int k,int x)
{
if(k==x) return true;//如果符合**元素
if(k>x) return false;//不可能达到
return search(2*k+1,x)||search(3*k+1,x);//按照式子查找
}
int **in()
{
int k,x;
scanf("%d,%d",&k,&x);
if(search(k,x)) printf("YES\n");//输出
else printf("NO\n");
return 0;
}
转载:感谢您阅览,转载请注明文章出处“来源从小爱孤峰知识网:一个分享知识和生活随笔记录的知识小站”。
链接:C++ 基础算法 - 递推 & 递归http://www.gufeng7.com/niaolang/1855.html
联系:如果侵犯了你的权益请来信告知我们删除。邮箱:119882116@qq.com
上一篇: C++ 基础算法 - 排序算法
下一篇: C++ 基础算法 - 搜索与回溯算法