C++算法之递归与递推(1)

一、递归(所有递归=>递归搜索树)


1.求斐波拉且数列
分析过程

执行是前序遍历,回溯是后序遍历,和栈的思想相同,先进后出

代码实现
#include<iostream>
using namespace std;
int f(int n)
{
    if(n==1) return 1;
    if(n==2) return 2;
    return f(n-1)+f(n-2);
}
int main()
{
    int n;
    cin>>n;
    cout<<f(n)<<endl;
    return 0;
}

2.AcWing 92.递归实现指数型枚举
分析过程

首先我们看数据量n=15,由数据范围反推时间复杂度2^{n}n\cdot 2^{n},因为从1~n,每个数有两种情况就有2^{n}种情况,每个方案长度是n,所以时间复杂度为n\cdot 2^{n}

其次递归考虑顺序很重要,我们从1~n依次考虑每个数选或不选,记得要考虑每次递归结束和恢复现场。

代码实现
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=16;
int n,st[N];//状态,记录每个位置当前的状态,0表示还没考虑,1表示选择,2表示不选择
void dfs(int u)
{
    if(u>n)//我们从1开始递归,然后递归会到下一位,此为结束条件
    {
        for(int i=1;i<=n;i++)
        {
            if(st[i]==1)
             printf("%d ", i);
        }
            printf("\n");
            return;//一个小分支的结束
    }
    //从第一个开始分两种情况进行递归
    //第一个选择
    st[u]=1;
    dfs(u+1);
    st[u+1]=0;//恢复现场
    //第一个不选择
    st[u]=2;
    dfs(u+1);
    st[u+1]=0;//恢复现场
}
int main()
{
    cin>>n;
    
    dfs(1);
    return 0;
}

3.AcWing 94.递归实现排列型枚举
分析过程

①依次枚举每个数放哪个位置

②依次枚举每个位置放哪个数

代码实现
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=16;
int n,state[N];//0表示未使用,1~n表示放了哪个数
bool used[N];//true表示用过,false表示没有用过
void dfs(int u)
{
    if(u>n)//边界
    {
        for(int i=1;i<=n;i++)
        {
             printf("%d ", state[i]);//打印方案
        }
        cout<<endl;
            return;//一个小分支的结束
    }
    for(int i=1;i<=n;i++)
    {
        if(!used[i])
        {
            state[u]=i;
            used[i]=true;
            dfs(u+1);
            
            //恢复现场
            state[u]=0;
            used[i]=false;
        }
    }
}
int main()
{
    cin>>n;
    
    dfs(1);
    return 0;
}

注:此题也可采用STL知识用next_permutation函数来做

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e8;
int a[N];
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)a[i] = i;
	do {
		for (int i = 1; i <= n; i++) printf("%d ", a[i]);
		puts("");
	} while (next_permutation(a + 1, a + n + 1));
	return 0;
}

4.AcWing 93.递归实现组合型枚举
分析过程

①画出递归搜索树

②搜索数=>代码实现=>dfs(参数):1)位置 way[N] 2)当前该枚举哪个位置u 3)start 当前应该从哪个数开始枚举

注:此题dfs可优化,采用剪枝的方法,速度可快三倍!

代码实现
#include<iostream>
#include<cstdio>

using namespace std;
const int N=25;
int way[N],n,m;
void dfs(int u,int start)
{
    if(u+n-start<m)return;//剪枝优化
    if(u>m)//结束条件
    {
        for(int i=1;i<=m;i++)printf("%d ",way[i]);
        puts("");
        return;
    }
    for(int i=start;i<=n;i++)
        {
            way[u]=i;
            dfs(u+1,i+1);
            //恢复现场
            way[u]=0;
        }
}

int main()
{
    scanf("%d%d",&n,&m);
    
    dfs(1,1);
    return 0;
}

5.AcWing 1209.带分数
分析过程

n=a+\frac{b}{c}\Rightarrow cn=ca+b\Rightarrow b=cn-ca

①枚举a dfs_a

②枚举c dfs_c

③判断b题是否成立

代码实现
#include<bits/stdc++.h>
using namespace std;
const int N=10;
typedef long long LL;
bool st[N],backup[N];
int n;
int ans;
bool check(int a,int c)
{
    LL b=n*(LL)c-a*c;
    if(!a||!b||!c)  return false;
    //拷贝,不影响原有数组
    memcpy(backup,st,sizeof(st));
    //取出b的每一位看是否在a,c种已经出现过
    while(b)
    {
        int x=b%10;//取出个位
        b=b/10;//删除个位
        if(backup[x]||!x) return false;//已经用过或者小于零
        backup[x]=true;
    }
    //再检查是否1~9每位数都已经使用过
    for(int i=1;i<=9;i++)
    {
        if(!backup[i])
        return false;
    }
    return true;
}
void dfs_c(int u,int a,int c)
{
    if(u>9) return;
    //检验等式是否满足
    if(check(a,c)) ans++;
    
    for(int i=1;i<=9;i++)
    {
        if(!st[i])
        {
            st[i]=true;
            dfs_c(u+1,a,c*10+i);
            //恢复现场
            st[i]=false;
        }
    }
}
void dfs_a(int u,int a)
{
    if(a>=n) return;
    if(a) dfs_c(u,a,0);
    
    for(int i=1;i<=9;i++)
    {
        if(!st[i])
        {
            st[i]=true;
            dfs_a(u+1,a*10+i);//将a变成数字的方法
            //还原现场
            st[i]=false;
        }
    }
}
int main()
{
    cin>>n;
    
    dfs_a(0,0);
    
    cout<<ans;
    return 0;
}

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
算法是一种通过已知的前一项值来导出下一项值的数学算法。在计算机科学中,算法常用于解决循环或归问题,通过将较大的问题拆解成较小的子问题来求解。 第三章讲解了算法的实现方法和应用场景。在C语言中,算法可以通过循环结构来实现。比如,斐波那契数列是一个经典的算法问题。通过定义前两项的初值为1,然后通过循环不断计算出下一项的值,最终得到一个斐波那契数列。 下面是一段使用算法计算斐波那契数列的C语言代码: ```c #include <stdio.h> int fibonacci(int n) { int a = 1; int b = 1; int c; if (n < 1) { return -1; // 输入不合法,返回错误代码 } else if (n < 3) { return 1; // 第一项和第二项的值为1 } for (int i = 3; i <= n; i++) { c = a + b; a = b; b = c; } return b; } int main() { int n = 10; int result = fibonacci(n); printf("第%d项的值为:%d\n", n, result); return 0; } ``` 通过循环计算,上述代码可以输出斐波那契数列的第10项的值为55。 算法在实际应用中还有很多其他的用途。比如,可以用算法来解决排列组合问题、动态规划问题等。在编程中,算法的优势在于其简单高效的计算方法,能够较快地求解问题。然而,有时候算法需要注意边界条件和初始值的设定,以避免出现错误的结果。 总结起来,算法是一种通过已知的前一项值来导出下一项值的数学算法。在C语言中,算法可以通过循环结构来实现。算法在计算机科学中有着广泛的应用,能够简化问题求解的过程,提高计算效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值