算法之路——DFS(我是如何悟道dfs模板的)

当我初入洛谷的DFS模块,我遇到的第一道题,就是经典的八皇后问题

从学完数据结构之后再没接触过算法,这次是为了备战蓝桥杯才去学算法,dfs不懂的我,陷入了巨大的沉思,只能在CSDN上摸索

但是!!!哥们我只想要dfs的模板啊,我怎么能想到你能想到的呢?慢慢悟吧,没办法了(哎)

接下来,进入正题

所以我选择了从一个最简单的题入手,这个题是我自己想的哈,就是从1 2 3 4 5里面任挑3个然后输出,即高中数学的A(5/3)

首先,我们需要一个全局变量n,和一个填入了(1,2,3,4,5)的数组,当然数组也要是全局的

还有一个全局的vis数组,用来标记是否访问过这个元素(这个下面会提到)

b数组用来存储答案,cns数组用来算总的结果数,不出意外的话:cns=6x5x4=60

int n,a[100],vis[100],b[100],cns;

然后就是简单的输入,这里不多赘述

扫描二维码关注公众号,回复: 16983821 查看本文章
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) //这里建议大家从1开始存哈,后面的step会方便一点
    {
        cin>>a[i];
    }
    dfs(1); //这个先别管,先看下面
    cout<<cns;
}

然后是核心要点:怎么去写dfs的函数呢?上模板!

①定义一个 step 变量,用来表示我们现在选的是第几个数 ( 比如dfs(1),那么代表着我现在正在选第1个数,还不知道花落谁家 )

void dfs(int step) //模板,可能不同的题会需要多个参数

②接着存数进b数组,我们到时候要输出,下面的这几行代码都是模板,请牢牢记住

for(int i=1;i<=n;i++) //注释一如下
{
    if(vis[i]) // 模板:判断vis数组中这个数有没有被选过,如果选过,就continue
    {
        continue;
    } 
    b[step]=a[i]; // 模板:存入最终要存输出的答案的数组
    vis[i]=1; // 已经选中它啦,所以要把vis数组的第i个置为1,表示我已经选过了
    dfs(step+1); //选下一个啦
    vis[i]=0; // 这里也是我当初学的时候很不理解的一个点,但是我后来悟道了,注释二
}

注释一状态:你的有关step的最终要存输出的答案的数组里最终要存的是什么?如果是(1,2,3,4,5)的a数组任选一个,那么就是要选择关于a数组的循环,这里选择从(1,n)循环遍历a数组

注释二这里为什么要重置呢?建议和下面的一起看哈!如果从程序的角度来说:

(1)我们先选了1,此时有关1的还有一个最外层的循环啊,就是(1,5)的这个循环

(2)然后深搜dfs(2),此时注意再次进了上面这个循环,因为vis【1】=1,所以不选,继续循环,i=2,vis【2】=0,所以可选,选中2

(3)同理继续深搜,vis【1】=vis【2】=1,vis【3】=0,选中3,注意:此时我们已经选了3个数了

(4)继续深搜:此时为dfs(4),选了3个数了,所以我们需要输出并且返回上一个选中3的循环,那么下一步就应该是vis【3】置为0

(5)这个时候你就会想啦,为啥要把它置为0呢?这是因为:3已经遍历过了,按道理来说,下一个是(1,2,4),但是你已经选中了(1,2,3)啦,你不丢掉一个,怎么选下一个呢,那么此时丢掉3,即vis【3】置为0,此时我们才2个数,还是可以寻找第3个数的

(6)那么这时,在我们最深层的循环中,会接着跳过3去遍历4

《可以画一个图帮助理解一下》

③什么时候return,输出呢,当然是选了3个数的时候啦,即step==4

if(step==4) //模板:填写输出阈值
{
   for(int i=1;i<4;i++) //输出
   {
       printf("%d ",b[i]);   
   }
   cns++; //看题目要求,可加可不加,如果题目需要先输出总数,再输出各种情况,那么就需要开个二维数组把情况都存起来,暂时就不能输出
   printf("\n");
   return ;
}

总代码如下

#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
using namespace std;
int n,a[100],vis[100],b[100],cns;
void dfs(int step)
{
    if(step==4)
    {
        for(int i=1;i<4;i++)
        {
            printf("%d ",b[i]);
            
        }
        cns++;
        printf("\n");
        return ;
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i])
        {
            continue;
         } 
        b[step]=a[i];
        vis[i]=1;
        dfs(step+1);
        vis[i]=0; 
    }
}
​
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    dfs(1);
    cout<<cns;
​
}

再来一题,是我继续学习dfs的时候碰到的和上面的差不多的题,就是高中数学的C(5/3)啦 :洛谷P1157

大家可以先别看,先自己试着写一下,我们难度慢慢递进

你可以发现,完完全全按模板来写就行

#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
#include <iomanip>
​
using namespace std;
int n,r;
int a[25],visit[25],b[25];
​
void dfs(int step){
    if(step==r+1){
        for(int i=1;i<=r;i++){
            cout<<setw(3)<<b[i];
        }
        cout<<endl;
        return;
    }
​
    for(int i=1;i<=n;i++){
        if(visit[i]==1){
            continue;
        }
        if(step==1 || i>b[step-1]) { //主要的区别就在这里,我们可以通过添加一些限制条件来控制我们的选择
            b[step] = i;
            visit[i] = 1;
            dfs(step + 1);
            visit[i] = 0;
        }
    }
}
​
int main(){
    cin>>n>>r;
    for(int i=1;i<=n;i++){
        a[i]=i;
    }
    dfs(1);
}

继续深入,看题(洛谷P2089 烤鸡)

大家可以先别看,先自己试着写一下,注意之前注释一中提到的状态,我们的最终数组里究竟要存的是什么?

#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
using namespace std;
int n;
int ans;
int a[15],b[10000][15],visit[15];
void dfs(int step,int sum){
    if(step>10){ //超出极限步数后就应要输出
        if(sum==n){ //进一步限制能输出的情况
            ans++;
            for(int i=1;i<=10;i++){
                b[ans][i]=a[i]; //需要先存入二维数组中,因为要先输出ans的值
            }
        }
        return;
    }
​
    for(int i=1;i<=3;i++){ //状态:你的step数组里最终要存的是什么?如果是(1,2,3)调料的克数,那么就是有关1-3的循环,如果是(1,2,3,4,5)中的任意一个数,那么就是a[i]循环该数组然后存入
        if(visit[step]==1){
            continue;
        }
        if(sum+i<=n){
            a[step]=i;
            visit[step]=1;
            dfs(step+1,sum+i);
            a[step]=0;
            visit[step]=0;
        }
    }
}
​
int main(){
    cin>>n;
    if(n<10 || n>30){
        cout<<"0";
    }else{
        dfs(1,0);
        cout<<ans<<endl;
        for(int i=1;i<=ans;i++){
            for(int j=1;j<=10;j++){
                cout<<b[i][j]<<" ";
            }
            cout<<endl;
        }
    }
​
}

再次深入——八皇后(洛谷P1219)图在文章开始

区别在于:记录遍历的数组vis增加,从1个变成了4个

#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
#include <vector>
#include <math.h>
#include <cstring>
using namespace std;
int n,j=2,cns;
int a[100],b[100],c[100],d[100];
int ans[100];

void dfs(int step){
    if(step>n){
        if(j>=0){
            for(int i=1;i<=n;i++){
                cout<<ans[i];
                if(i!=n){
                    cout<<" ";
                }
            }
            j--;
            cout<<endl;
        }
        cns++;
        return;
    }

    for(int i=1;i<=n;i++){  // i代表列
        if(!b[i] && !c[i+step] && !d[i-step+n]){
            ans[step]=i;
            b[i]=1;
            c[i+step]=1;
            d[i-step+n]=1;
            dfs(step+1);
            b[i]=0;
            c[i+step]=0;
            d[i-step+n]=0;
        }
    }
}

int main(){
    cin>>n;
    dfs(1);
    cout<<cns;
}

“算法虐我千百遍,我待算法如初恋”,加油,希望未来我们顶峰相见!!!收藏一下吧嘿嘿

猜你喜欢

转载自blog.csdn.net/m0_67227361/article/details/129478909
dfs