Phil的课堂笔记——DFS(搜索入门:暴力大法)

DFS(搜索入门:暴力大法)

在这里插入图片描述
祭一张老师的课件(逃~~~

对拍

  1. DFS
  2. 正解

目录

  1. 全排列

搜索=递归+枚举+回溯

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 版权协议,转载请附上原文出处链接及本声明。
原文链接:原文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值