DFS(搜索入门:暴力大法)
祭一张老师的课件(逃~~~
对拍
- DFS
- 正解
目录
- 全排列
搜索=递归+枚举+回溯
1 全排列
-
全排列方案数
- 祭数学方法
- 小学奥数之组合:插空插空插板
- 递归:
f[n]=n*f[n-1]
- 祭数学方法
-
全排列输出
如果给你n个数,请你输出全排列的所有结果
- 百度百科定义全排列:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
公式:全排列数f(n)=n!(定义0!=1)
那么怎么用递归程序实现呢?
我们来思考一下
- 首先我们想到的是plan 1;
bool b[105];//标记数组
void per(int m)
{
if(m==0) {
cout<<endl;
return;
}
for(int i=1;i<=n;i++){//写出首位的数
if(b[i]==0){ //判断是否用过
cout<<i;
b[i]=1; //打标记
per(m-1); //n-1个数进行全排列,首位数最小是多少
}
}
}
但是我们发这段代码是有些问题的,因为它输入3的时候,只能输出
123
为什么呢?
因为它相当于一连串的把123打了标记;所以这几个数就不能继续用来
所以我们有一个比较重要的步骤就是——回溯
- Therefore,plan 2 is given;
bool b[105];
void per(int m)
{
if(m==0) {cout<<endl;return;}
for(int i=1;i<=n;i++)//写出首位的数
{
if(!b[i])//如果这个位置的数没有用过,则可以用
{
b[i]=1;
cout<<i;
per(m-1);//n-1个数进行全排列,首位数最小是多少
b[i]=0;//+++++++++++++++回溯!!!!
}
}
}
在12行的位置上,需要我们进行回溯;回溯的意义就在于,我们进行完这一步,可以回到上一步的情况继续枚举;
如123的排列问题中:
排完了123;如果进行回溯,那么枚举完23,就可以一步步的将打过标记的23消除标记;从而实现132
the result is
其他的第一位数去哪里了呢???
所以我们还需考虑输出的问题
- so,plan 3
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,c[105];
bool b[105];
void print(int k) //这里是一个输出的函数
{
for(int i=1;i<=k;i++)
printf("%d ",c[i]);
cout<<endl;
}
void per(int t)
{
if(t==n+1) {print(t-1);return;} //这里改了!
for(int i=1;i<=n;i++) //写出首位的数
{
if(!b[i]) //如果这个位置的数没有用过,则可以用
{
b[i]=1; //把数打了标记 //区别于下一行
c[t]=i; //用一个数组来存列举的每个情况 //把位置输出
per(t+1); //n-1个数进行全排列,首位数最小是多少
b[i]=0; //回溯
}
}
}
int main()
{
cin>>n;
per(1); //1排到t+1和 n排到0 的效果是一样的
}
这里有一个变化,n排到0 变成了1排到t+1
其实1排到t+1和 n排到0 的效果是一样的;
但是由于输入辅助数组时方便,所以用了正序
!!!总结一下
模板
void dfs()//参数用来表示状态
{
if(到达终点状态)
{
...//根据题意添加
return;
}
if(越界或者是不合法状态)
return;
if(特殊状态)//剪枝
return ;
for( 扩展方式 )
{
if( 扩展方式所达到状态合法 )
{
修改操作;//根据题意来添加
标记;
dfs();
(还原标记);
//是否还原标记根据题意
//如果加上(还原标记)就是 回溯法
}
}
}
————————————————
版权声明:本代码为CSDN博主「F.F.Chopin」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:原文