scau 18928 递归实现全排列

搜索算法真的太难了啊啊啊啊啊!

递归实现排列枚举、递归实现指数型枚举、递归实现组合枚举真的一言难尽。关于递归实现排列枚举我在网上看了相关算法写法(简单的代码没有解释!有解释的文章代码复杂!谁懂啊!),代码很短,但是不停地递归递归,真的好难get到那个点,于是乎,我花了一个半小时,在草稿纸上进行递归演示,终于迷迷糊糊的懂了一点。

const int N=10;

int a[N],n,path[N];
//a[]为待枚举的序列,n为序列元素的个数,path[]存储dfs的路径结果
bool vis[N];
//布尔数组用来判断在dfs过程中序列元素是否已经被放入path[]中

void dfs(int u)
{
    if(u==n)
    {
        for(int i=0;i<n;i++)
            cout << path[i] << ' ';
        cout << endl;
    }
    for(int i=0;i<n;i++)
    {
        if(!vis[i])//序列元素没有放进path[]
        {
            /*if(i && a[i]==a[i-1] && !vis[i-1])
                continue;//后面的语句都不执行
            //剪掉重复分支*/
            vis[i]=true;//放进path[]
            path[u]=a[i];
            dfs(u+1);
            path[u]=0;//回溯
            vis[i]=false;
        }
    }
}

这就是代码段,下面举个简单的小例子。

比如输入2,那么数组a[]里就存两个数:1和2。在主函数中,首次调用dfs(0)。

第一步第一次递归,u=0,n=2,不满足函数开头的if语句,进行for循环。发现vis[0]没有标记过,那就先把它给标记了,令path[0]=a[0]=1(数组path[]起到记录的作用,递归结束后再清除),调用dfs(0+1)。

第二步第二次递归,u=1,n=2,依旧不满足函数开头的if语句,进行for循环。vis[0]标记过,检查vis[1],没有标记过,把vis[1]先标记了,令path[1]=a[1]=2,调用dfs(1+1)。

第三步第三次递归,u=2,n=2,满足函数开头的if语句了,输出path数组,得到了1 2。进行for循环的时候,发现vis[0] vis[1]都不满足for循环中的if语句了,那么返回到第二次中dfs(1+1),执行其后的语句

第四步第三次递归结束返回到第二次递归中的递归函数后面,令path[1]=0(这句不写也没太大关系,反正后面会被新的值覆盖掉),最重要的是vis[1]=false(取消选中,如果不做这一步的话,后面就永远无法再选,也就是你越递归,你原数组a[]中的值就越少),此时不满足for循环的条件了,回到第一次递归的递归函数后面

第五步,此时,回到了第一次递归函数的后面,令path[0]=0,vis[0]=false,此时的i=0,还满足for循环条件,那么vis[1]在第四步时取消选中了,令vis[1]=true(选中),path[0]=a[1]=2,再调用dfs(0+1)。

第六步,进行for循环的时候,vis[0]!=1,满足if条件,令vis[0]=true,path[1]=a[0]=1,调用dfs(1+1)。

第七步,u=2,n=2,u==n,输出path得到2  1,此时vis[0]和vis[1]都被标记了,整个递归全部结束。

我在草稿纸上就是这么算的,在这里我就复述了一遍过程(递归结束的那些u啊i啊挺容易搞乱的,要多注意下),但可能文字比较多,看起来容易没耐心,我本来想画个图的,别看只有两个数,但是画起来也很复杂,所以说递归实现枚举的时间复杂度特别高,在我用草稿本演算了一遍之后深有体会,在我的每篇博客基本都有说:不要头脑编译!一定要自己动手画画!

好啦,就这样!

下面是全部代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;

const int N=10;

int a[N],n,path[N];
//a[]为待枚举的序列,n为序列元素的个数,path[]存储dfs的路径结果
bool vis[N];
//布尔数组用来判断在dfs过程中序列元素是否已经被放入path[]中

void dfs(int u)
{
    if(u==n)
    {
        for(int i=0;i<n;i++)
            cout << path[i] << ' ';
        cout << endl;
    }
    for(int i=0;i<n;i++)
    {
        if(!vis[i])//序列元素没有放进path[]
        {
            /*if(i && a[i]==a[i-1] && !vis[i-1])
                continue;//后面的语句都不执行
            //剪掉重复分支*/
            vis[i]=true;//放进path[]
            path[u]=a[i];
            dfs(u+1);
            //path[u]=0;//回溯
            vis[i]=false;
        }
    }
}
int main()
{
    cin >> n;
    for(int i=0;i<n;i++)
    {
        a[i]=i+1;//把1--n的数字放进数组a[]
    }
    sort(a,a+n);//升序排列,使所有相同重复元素相邻
    dfs(0);
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值