搜索算法真的太难了啊啊啊啊啊!
递归实现排列枚举、递归实现指数型枚举、递归实现组合枚举真的一言难尽。关于递归实现排列枚举我在网上看了相关算法写法(简单的代码没有解释!有解释的文章代码复杂!谁懂啊!),代码很短,但是不停地递归递归,真的好难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;
}