·搜索篇-深度优先搜索第一回[全排列篇]c++

1.深度优先搜索 又称 dfs

深搜,顾名思义 就是以深度为上的搜索算法,它本质上就是个递归

只不过是一个比较奢华的的递归

深搜的使用背景 &基本知识点

深搜简称使用背景参考模型题型
dfs找出所有解;全排列;找到所用的方法数;图论;栈和递归全排列;二维深搜;

TIPS:dfs一般从1开始搜索,与常用的递归不同,有些深搜中是不需要调用自身的,比如下面这道题:

洛谷传送门:P1706 全排列问题

密码岛OJ传送门:#1244. 全排列

全排列问题

题目描述

按照字典序输出自然数 1到 n所有不重复的排列,即 n的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入格式

一个整数 n。

输出格式

由 1∼n 组成的所有不重复的数字序列,每行一个序列。

每个数字保留5个场宽。

样例 #1

样例输入 #1

3

样例输出 #1

    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

提示

1≤n≤9。

这道题的本意就在于求排列,类似于排列组合里的A 每个数都要排不止一次。
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。——百度百科

所以通过题目的意思我们可以知道,全排列的次数的出口在于当m=n时,就结束

我们看到题目,第一反应一定是用循环做。比如本蒟蒻一开始的想法:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cout<<i;
    } 
    return 0;
}

可是情况不是很对路。

人家要的是全排列!!全排列!!你这就只输出了个数!!

没关系 ,事情要从根本入手。我们看看如果要一次输出两个数该怎么样。

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<<i<<" "<<j;
        } 
    } 
    return 0;
}

一个非常没有技术含量 非常Wonderful的枚举。

如果一次要输出三个数怎么办??

#include<bits/stdc++.h>
using namespace std;
 int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int k=1;k<=n;k++){
                cout<<i<<" "<<j<<" "<<k;
            } 
        } 
    } 
    return 0;
}
//水印

一个很没有技术含量的枚举。这时候你已经面临TLE 的危险了。

这时候,你很无厘头的想到,再这么枚举下去你也不知道要写几个循环。

于是你想用函数,但是你又不知道函数要调用几次。

这时深搜它出现了

但深搜怎么写,第一步你还是只知道用循环。那就写个循环8

for(int i=1;i<=n;i++){
        printf("%5d",a[i]);
    }
    cout<<endl;
}

非常Wonderful。珂朵莉都给你点赞。

但是你还要控制范围,所以再写一个if:

if(x>n){
    for(int i=1;i<=n;i++){
        printf("%5d",a[i]);
    }
    cout<<endl;
}

但是你这只是满足了输出的情况,你还要判断如果不能输出的时候应该执行什么操作,所以我们可以用else语句:

else{ }

但是在else里面写什么??回到深搜最开始的题面

题目想让你干什么 要输出1-n的全排列 如果保证不重复的情况下,那你还需要一个标记的东西来标记它是否重复出现过。

用什么标记?用单个变量吗 必然是不行的。人家有一堆数要你排 那就开个数组8

bool b[1000]={0};

老样子,一个 for循环。先判断它是否被使用过,也就是判断b[i]是否==1。如果是的话就说明被使用过,就直接continue,剪枝。别忘了遍历完要再标记成1.

else{
    for(int i=1;i<=n;i++){
        if(b[i]==1){
            continue;
        }
        b[i]=1;
    }
}

标记之后呢?你深搜完之后说明,在这一次操作里你的数已经排完了,你要开始重新挑三个数了。那这时候你就要把原本标记的1改成0。

else{
    for(int i=1;i<=n;i++){
        if(b[i]==1){
            continue;
        }
        b[i]=1;
        b[i]=0;
    }
}

你既然要做深搜,那就看回上面输出的时候你输出了什么。你输出了a[i]. 那你肯定就要把你的value存进a[ ]数组里,由于x是每层递减的,象征了搜索的次数,所以你大可以用x当做a的下标,然后存i.

深搜部分完整代码如下:

int n;
int a[100];
bool b[1000]; 
void dfs(int x){
    if(x>n){
        for(int i=1;i<=n;i++){
            printf("%5d",a[i]);
        }
        cout<<endl;
    }else{
        for(int i=1;i<=n;i++){
            if(b[i]==1){
                continue;
            }
            b[i]=1;
            a[x]=i;
            dfs(x+1);
            b[i]=0;
        }
    }
} 

但是你还要有main函数的呀 main是从1开始搜,所以加上主函数整道题的完整代码如下:

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<climits>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
int n;
int a[100];
bool b[1000]; 
void dfs(int x){
    if(x>n){
        for(int i=1;i<=n;i++){
            printf("%5d",a[i]);
        }
        cout<<endl;
    }else{
        for(int i=1;i<=n;i++){
            if(b[i]==1){
                continue;
            }
            b[i]=1;
            a[x]=i;
            dfs(x+1);
            b[i]=0;
        }
    }
} 
int main(){
    cin>>n;
    dfs(1);
    return 0;
}

完美,AC 撒花,这道题就完美的过了。

今天是全排列篇,不想写了 写太长了,下次再讲拓展8

888888888888886

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值