一.递推
在C++中,递推算法可以通过循环或递归来实现。本文将详细介绍C++中的递推算法实现方式。
- 循环实现递推算法: 循环是一种常用的实现递推算法的方法。通过定义初始条件和更新规则,在每次迭代中计算出后续项的值
#include <iostream> int main() { int n; // 要计算的项数 int a = 0, b = 1; // 初始条件 std::cout << "请输入要计算的项数:"; std::cin >> n; std::cout << "斐波那契数列前" << n << "项为:"; std::cout << a << " " << b << " "; for (int i = 2; i < n; i++) { int temp = a + b; std::cout << temp << " "; a = b; b = temp; } std::cout << std::endl; return 0; }
上述代码使用循环计算斐波那契数列的前n项。通过定义初始条件a=0、b=1,然后使用for循环从第3项开始计算,每次迭代计算出当前项的值temp,并更新a和b的值。
- 递归实现递推算法: 递归是一种自身调用的方法。通过定义递归函数,在每次调用函数时传入不同的参数,实现对问题的分解和求解。
上述代码使用递归函数计算斐波那契数列的前n项。递归函数fibonacci接收一个参数n,如果n为0或1,则返回相应的初始条件;否则,递归调用fibonacci函数来计算前两项的和。
-
#include <iostream> int fibonacci(int n) { if (n == 0) return 0; else if (n == 1) return 1; else return fibonacci(n - 1) + fibonacci(n - 2); } int main() { int n; // 要计算的项数 std::cout << "请输入要计算的项数:"; std::cin >> n; std::cout << "斐波那契数列前" << n << "项为:"; for (int i = 0; i < n; i++) { std::cout << fibonacci(i) << " "; } std::cout << std::endl; return 0; }
-
无论是循环还是递归实现,递推算法都可以根据给定的初始条件和更新规则,通过迭代或递归的方式计算出后续项的值。具体选择哪种实现方式取决于问题的特点和性能需求
-
通常情况下,用循环实现递推比用递归实现递推的时间复杂度要低一些,因为递归有时会多次重复计算相同数据,导致时间复杂度过高。所以,我们通常在递归中增加记忆化搜索,避免重复计算。
-
#include <iostream> using namespace std; int fibo[10005]={0,1};//记忆化数组 int fibonacci(int n) { if(fibo[n]!=0) return fibo[n];//判断是否进行过记忆化 for(int i=2;i<=n;i++) fibo[i]=fibo[i-1]+fibo[i-2];//记忆化处理 return fibo[n]; } int main() { int t;// 要输入的次数 int n;// 要计算的项数 cin>>t; while(t--) { scanf("%d",&n); printf("%d\n",fibonacci(n)); } return 0; }
既然学会了,那就上两道题
-
Pell数列a_1,a_2,a_3, ...的定义是这样的,a_1 = 1, a_2 = 2, ... , a_n = 2 a_{n−1} + a_{n-2}(n>2)。
给出一个正整数k,要求Pell数列的第k项模上32767是多少。
-
输入:第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数k (1≤k<1000000)
-
输出:n行,每行输出对应一个输入。输出应是一个非负整数。
-
样例输入
2 1 8
- 样例输出
1 408
-
分析:这道题其实就是一个斐波那契数列的变形,唯一需要注意的是数据范围,话不多说,直接上代码:
-
#include<cstdio> const int maxN = 1000001; int a[maxN]; void lcc(){ a[1]=1; a[2]=2; for(int i=3;i<maxN;i++) a[i]=(2*a[i-1]+a[i-2])%32767; } int main(){ int n; lcc(); scanf("%d",&n); for(int i=0;i<n;i++){ int k; scanf("%d",&k); printf("%d\n",a[k]); } return 0; }
楼梯有n(81>n>0)阶台阶,上楼时可以一步上1阶,也可以一步上2阶,也可以一步上3阶,编程 计算共有多少种不同的走法。
输入:输入的每一行包括一组测试数据,即为台阶数n。最后一行为0,表示测试结束。
输出:每一行输出对应一行输入的结果,即为走法的数目。
样例输入:
1 2 3 4 0
样例输出:
1 2 4 7
分析:这道题看似很复杂,其实只要找对递推式,就会发现这道题很简单。从一次可以上1~3个台阶看出,f[i]=f[i-1]+f[i-2]+f[i-3]。这样一来这道题立马简单多了。话不多说,直接上代码:
#include <cstdio>
#include <cstring>
using namespace std;
int a = 1;
long long A[75];
int main(){
A[0] = 1;
for(int i = 1; i <= 72; i ++)
{
if(i - 3 >= 0)
A[i] = A[i - 1] + A[i - 2] + A[i - 3];
else if(i - 2 >= 0)
A[i] = A[i - 1] + A[i - 2];
else if(i - 1 >= 0)
A[i] = A[i - 1];
}
while(a != 0)
{
scanf("%d", &a);
if(a != 0)
printf("%lld\n", A[a]);
}
return 0;
}
总结:递推其实并不复杂,复杂的是找对递推式,只要递推式找对,题目就迎刃而解了。
二.递归
在C++中,递归是一种常用的编程技巧,它允许函数在其自身内部调用自身。递归可以解决需要重复执行相同或类似操作的问题,通过将问题分解为更小的子问题来实现。
下面是一个使用递归算法计算阶乘的示例:
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0)
return 1;
else
return n * factorial(n - 1);
}
int main() {
int num;
cout << "请输入一个非负整数: ";
cin >> num;
if (num < 0) {
cout << "输入错误,请输入非负整数!" <<endl;
return 0;
}
int result = factorial(num);
cout << num << " 的阶乘是:" << result <<endl;
return 0;
}
在上述代码中,我们定义了一个递归函数factorial
来计算给定整数n
的阶乘。如果n
为0,则返回1(阶乘的边界条件)。否则,递归调用factorial
函数来计算n-1
的阶乘,并将其与n
相乘,得到n
的阶乘。
需要注意的是,在使用递归时需要设置递归的终止条件,即递归函数应该能够在某个条件下终止递归。否则,递归将无限进行下去,导致堆栈溢出等问题。
递归在解决问题时具有简洁、直观的优势,但也需要注意一些潜在的问题。递归可能会占用大量的内存空间和堆栈空间,并且在某些情况下可能会导致性能问题。因此,在使用递归算法时,需要合理地选择递归的深度和递归调用的位置,以避免潜在的问题。
总结来说,递归是一种常用的编程技巧,通过函数在自身内部调用自身,可以解决需要重复执行相同或类似操作的问题。在使用递归时,需要设置递归的终止条件,并注意内存和性能方面的考虑。
众所周知,学算法,不做题是学不会的,让我们上两道题:
逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的逆波兰表示法为* + 2 3 4。本题求解逆波兰表达式的值,其中运算符包括+ - * /四个。
输入:输入为一行,其中运算符和运算数之间都用空格分隔,运算数是浮点数。
输出:
输出为一行,表达式的值。
可直接用printf("%f\n", v)输出表达式的值v。
样例输入:
* + 11.0 12.0 + 24.0 35.0
样例输出:
1357.000000
分析:这道题其实就是每一个运算符和两个数组成算式,这道题不仅可以使用递归,使用数据结构栈也十分简洁,但用栈解决的代码我们以后再讨论:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
double exp()
{
char a[10];
scanf("%s",a);
switch(a[0])
{
case '+':return exp()+exp();
case '-':return exp()-exp();
case '/':return exp()/exp();
case '*':return exp()*exp();
default:return atof(a);
}
}
int main()
{
double ans;
ans=exp();
printf("%f\n",ans);
return 0;
}
总结:递归的精髓就是一句话,自己调用自己。
最后,让我们以一道递归结尾:
输入一个数,输出其素因子分解表达式。
输入:输入一个整数 n (2 <= n < 100)。
输出:输出该整数的因子分解表达式。
表达式中各个素数从小到大排列。
如果该整数可以分解出因子a的b次方,当b大于1时,写做 a^b ;当b等于1时,则直接写成a。
样例输入:
60
样例输出:
2^2*3*5
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int n;
scanf("%d",&n);
for(int i=2;n!=1;i++)
{
int cnt=0;
while(n%i==0)
{
n/=i;
cnt++;
}
if(cnt!=0)
{
if(n==1)
{
if(cnt==1) printf("%d\n",i);
else printf("%d^%d\n",i,cnt);
break;
}
else
{
if(cnt==1) printf("%d*",i);
else printf("%d^%d*",i,cnt);
}
}
}
return 0;
}