递归算法的应用

递归算法的应用


递归实现排列型枚举

题目描述

在这里插入图片描述


核心思路

可以用递归算法来求解,也可以用STL中的next_permutation函数

在这里插入图片描述


代码

#include<iostream>
using namespace std;
const int N=10;
int path[N];    //存储全排列的方案
bool st[N]; //标记某个数字是否已经被使用过了
int n;
//u表示当前枚举到第几个位置了
void dfs(int u)
{
    //u=0,表示第1个位置,u=n-1表示第n个位置,从0到n-1一共有n位
    //因此如果u==n,说明当前正在枚举第n+1个位置,那么就说明了前面n位已经枚举完成了
    if(u==n)
    {
        //输出该种合法枚举的答案
        for(int i=0;i<n;i++)
            printf("%d ",path[i]);
        cout <<endl;
        return ;    //回溯
    }
    //每个位置都可以选择1到n中的某个数字填放到该位置上
    for(int i=1;i<=n;i++)
    {
        //如果i这个数字还没有被使用过
        if(!st[i])
        {
            st[i]=true;//标记i这个数字被使用过了
            path[u]=i;  //在第u个位置上放了数字i
            //递归下一个位置
            dfs(u+1);
            //恢复现场
            st[i]=false;//原来i这个数字还没有被使用过
            path[u]=0;//原来第u个位置上放的是数字0
        }
    }
}
int main()
{
    scanf("%d",&n);
    //
    dfs(0);
    return 0;
}

递归实现组合型枚举

题目描述

在这里插入图片描述


核心思路

全排列是需要考虑顺序的,但是组合是不需要考虑顺序的。即对于全排列来说,123、132、213、231、312、321是六种不同的方案,但是对于组合来说,它们就只是相同的一种方案而已(因为都是选出了1,2,3这三个数字)。

既然123、132、213、231、312、321对于组合枚举来说都是相同的,那么我们应该选择哪一个作为结果呢?因为题目要求从小到大输出所有方案,因此我们可以选择123这一组作为方案。

问题:对于组合枚举来说,怎样才能选出正确的方案呢?

我们可以在枚举时,人为地定义一个规则:即组内是按从小到大排序的。比如123,组内就是满足从小到大排序,但是132、213、231、312、321就都不满足组内从小到大排序。因此,在我们在递归时,如果发现下一个位置的数字比上一个位置的数字还要小,那么我们就不选择走这个分支,因为一旦选择这个分支,得到的方案必定是不满足组内从小到大排序的这个要求的。

在这里插入图片描述


代码

#include<iostream>
using namespace std;
const int N=30;
int path[N];    //存储组合枚举的方案
int n,m;
//u表示当前正在枚举的位置,start表示当前的这个位置上的数字最小该从啥开始枚举
void dfs(int u,int start)
{
    //选出来的总的数的个数不足m个,提前回溯
    if(u+n-start<m)
        return;
    //当u==m+1(即u>m时),说明从u=1到u=m,枚举填完了这m个位置,那么可以回溯了
    if(u>m)
    {
        for(int i=1;i<=m;i++)//输出方案
        {
            printf("%d ",path[i]);
        }
        cout <<endl;
        return;
    }
    //当前位置上的数字可以从[start,n]中选择。
    for(int i=start;i<=n;i++)
    {
        path[u]=i;  //在u这个位置上放入i这个数字
        //递归到下一个位置  下一个位置上的start是当前这个位置上的数字+1
        dfs(u+1,i+1);
        //恢复现场
        path[u]=0;//原来u这个位置上的数字是0
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    //从第1个位置开始枚举,当前start最小可以选1
    dfs(1,1);
    return 0;
}
//这段代码可以实现:从n个数中随意选出m个数时的各种方案
#include<iostream>
using namespace std;
int n,m,path[30];
int a[30];
//s枚举位置(是数组的下标)  c表示选了多少个数
void dfs(int s,int c)//按字典序输出n个数选m个数的所有组合
{
    //c从0到m-1,一共选了m个数,因此当c=m时,说明到了m+1个数,那么就保证也已经选出了m个数
    //因此就可以输出这次选择的方案了
    if(c==m)
    {
        for(int i=0;i<m;i++)
            cout<<path[i]<<" ";
        cout<<endl;
        return ;
    }
    cout <<endl;
    for(int i=s;i<n;i++)
    {
        path[c]=a[i];//第c个选的数字是a[i]
        //继续枚举下一个位置i+1 然后看看第c+1个要选的数是啥
        dfs(i+1,c+1);
    }
}
int main()
{
    cin >>n>>m;
    for(int i=0;i<n;i++)
        cin >>a[i];
    dfs(0,0);
    return 0;
}

递归实现指数型枚举

题目描述

在这里插入图片描述


核心思路

这题与组合枚举不同,组合枚举是从n个数中选出m个数。但是这个题是从n个数中选出任意多个,任意多个并未指明是多少个。

由于题目要求升序排列,因此我们可以这么做:从1~n,依次考虑每个数或者不选。填坑,从填1个坑到填n个坑。

在这里插入图片描述


代码

#include<iostream>
using namespace std;
const int N=16;
int st[N];// 状态,记录每个位置当前的状态:0表示还没考虑,1表示选它,2表示不选它
int n;
//u表示当前正在枚举的数
void dfs(int u)
{
    //如果u==n+1(即u>n),则说明从u=1到u=n,这n个位置上的数字都选出来了。
    if(u>n)
    {
        for(int i=1;i<=n;i++)//输出这n个位置山的数字
        {
            if(st[i]==1)//如果选择了i这个数字,则输出
            {
                printf("%d ",i);
            }
        }
        cout <<endl;
        return ;
    }
    //第一个分支:不选u这个数字
    st[u]=2;
    dfs(u+1);//递归下一个
    st[u]=0;    //恢复现场

    //第二个分支:选择u这个数字
    st[u]=1;
    dfs(u+1);//递归下一个
    st[u]=0;//恢复现场
}
int main()
{
    scanf("%d",&n);
    //从数字1开始枚举
    dfs(1);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值