拿下DFS,小小蓝桥杯

蓝桥杯不会DFS就好像刘备失去了诸葛亮一样,啊啊啊啊啊DFS真的很重要啊,要不然我也不会花时间在这总结,可恶,总结完我就去看熊出没大电影去。好了,来看一下蓝桥杯最常考的DFS类型题吧!!!

1.DFS实现排列型枚举

核心思路:枚举每个位置应该填哪一个数。

先来看一下排列什么意思:从 n 个不同元素中,任取 m(m≤n) 个元素,按照一定的顺序排成一列,叫做从 n 个不同元素中取出 m 个元素的一个排列。

就是要考虑顺序,求全排列是很经典的一个dfs模板题,暴力枚举的思路是这样的,m个位置,依次枚举每一个位置应该填哪一个数。

public class Test14 {
     static int N=1000000;
     static int[]a=new int[N];
     static boolean[]st=new boolean[N];
     static int n;
    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);
        n  = sc.nextInt();
        dfs(1);
        


    }
    public static void dfs(int u){
        if(u>n){
            for (int i =1; i <=n ; i++) {
                System.out.print(a[i]+" ");
            }
            System.out.println();
            return;
        }

        for (int i=1;i<=n;i++){
            if(!st[i]){
                a[u]=i;
                st[i]=true;
                dfs(u+1);
                st[i]=false;
            }
        }

    }
}

蓝桥杯考全排列的思路多了去了,学会这个算法就可以做蓝桥杯里面的很多题了,因为蓝桥杯听说也被称为暴力杯,考DFS的题很多很多.......

光说不练也不行,下面来个典型例题练练手,链接在这里1.带分数 - 蓝桥云课 (lanqiao.cn)

很容易想到暴力的思路,n=a+b/c,可以求出1-9的全排列然后枚举加法和乘法的位置,判断等式是否成立。

求全排列我们已经掌握了,那么如何枚举加号和除号的位置呢?

很显然,一共九个数,加号的前面只能有1-7个数,除号可以紧跟加号后面,但最多要给C留一位数,经过分析,不难写出下面的这段代码:

public static void check(){
        for (int i = 1; i <=7 ; i++) {
            for (int j = i+1; j <=8 ; j++) {
                a=toInt(1,i);
                b=toInt(i+1,j);
                c=toInt(j+1,9);
            }
            if(n*c==a*c+b){
                ans++;
            }
        }
    }

toInt函数的实现也很简单,如下

public static int toInt(int start,int end){
        int num=0;
        for (int i = start; i<=end ; i++) {
            num=num*10+arr[i];
        }

        return num;
    }

实现了这些,这道题对我们来说不就轻轻松松了吗?

拿下这道题之后,我们可以趁热打铁试一下这几道DFS的题目,相信我们更能加深对dfs算法的理解:

16.纸牌三角形 - 蓝桥云课 (lanqiao.cn)

0凑算式 - 蓝桥云课 (lanqiao.cn)

0三羊献瑞 - 蓝桥云课 (lanqiao.cn)

5.分糖果 - 蓝桥云课 (lanqiao.cn)

2.递归实现指数型枚举

核心思路:枚举每一个thing的状态;

从 1∼n1∼� 这 n� 个整数中随机选取任意多个,输出所有可能的选择方案。

每个数有若干种状态,我们只需要枚举每个数的每一个状态就可以了,对于本道题,每个数只有选或不选两种状态,因此我们可以考虑使用DFS解决。


import java.util.ArrayList;
import java.util.Scanner;

//递归实现指数型枚举;
public class Main {
    static int N=16;
    static int n;
    static int[]st=new int[N];
    static ArrayList<ArrayList<Integer>>res=new ArrayList<>();
    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);
        n = sc.nextInt();
        dfs(1);
        for (int i=0;i<res.size();i++){
            for (int j=0;j<res.get(i).size();j++){
                System.out.print(res.get(i).get(j)+" ");
            }
            System.out.println();

        }

    }


    public static void dfs(int u){
        if(u>n){
            ArrayList<Integer>way=new ArrayList<>();
           for (int i=1;i<=n;i++){
             if(st[i]==1){
                 way.add(i);
             }
           }
           res.add(way);
           return;

        }


        st[u]=2;
        dfs(u+1);
        st[u]=0;

        st[u]=1;
        dfs(u+1);
        st[u]=0;


    }
}

这里的st数组有三个值,0---代表还没考虑,1-----代表选这个数,2------表示不选当前这个数,dfs之后不要忘了回溯。

下面上真题:

0有奖问答 - 蓝桥云课 (lanqiao.cn)

面对这种题,我们会DFS!!!

依次枚举每道题,每道题有正确或错误两种状态,这样DFS的参数我们也可以很轻松的明确,dfs(int u,int score)  u表示当前正在枚举第几道题目,score则表示当前获得的分数。

写好了参数之后,来写一下递归出口吧!

if(u>31){

            return;
        }
        if(score==100){

            return;
        }

        if(score==70){
            ans++;
        }
下面枚举答题正确和错误两种状态
  dfs(u+1,score+10);

        dfs(u+1,0);

怎么样?意犹未尽吗?那就再来几道题吧!!!

1.买瓜 - 蓝桥云课 (lanqiao.cn)

1.砝码称重 - 蓝桥云课 (lanqiao.cn)

0李白打酒 - 蓝桥云课 (lanqiao.cn)

3.递归实现组合型枚举

组合与排列不同,组合不考虑顺序.

组合数公式是指从 n 个不同元素中,任取 m(m≤n) 个元素并成一组,叫做从 n 个不同元素中取出 m 个元素的一个组合;从 n 个不同元素中取出 m(m≤n) 个元素的所有组合的个数,叫做 n 个不同元素中取出 m 个元素的组合数。用符号 C(n,m) 表示。(百度百科)

和枚举排列型的时候类似,那么我们怎么去重呢,因为1 2 3 和 2 3 1 and 3 2 1我们都会搜到,我们只想要一个组合就好了呀!

我们可以只需要数字从小到大的排列,再写函数参数的时候加上一个start,来表示当前位置从哪个数开始枚举,就可以完全解决这个问题;

dfs(int u,int start)   u表示正在枚举第几个位置,start表示从哪一个数字开始枚举。

下面上代码:


public class Test02 {
    //递归实现组合型枚举;
    static int N=20;
    static int[]a=new int[N];
    static int n,m;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
         n = sc.nextInt();
         m=sc.nextInt();

         dfs(1,1);


    }

    public static void dfs(int u,int start){
        if(u+n-start<m){
            //如果把后面的选完也不能凑够n个数,剪枝;
            return;
        }
        if(u>m){
            for (int i = 1; i <=m ; i++) {
                System.out.print(a[i]+" ");
            }
            System.out.println();
        }

        for (int i = start; i <=n ; i++) {
            a[u]=i;
            dfs(u+1,i+1);
            a[u]=0;

        }





    }

}

怎么样,是不是感觉到了DFS的神奇之处了呢,下面我们说这种类型的题目,不同的是这道题用到了并查集这个小东西。

4.DFS+并查集

5.DFS走迷宫

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * (有向)图的深度优先遍历算法模板 */ package dsa; public abstract class DFS extends GraphTraverse { //变量 protected static int clock = 0;//遍历过程中使用的计时钟 //构造方法 public DFS(Graph g) { super(g); } //深度优先遍历算法 protected Object traverse(Vertex v, Object info) {//从顶点v出发,做深度优先查找 if (UNDISCOVERED != v.getStatus()) return null;//跳过已访问过的顶点(针对非连通图) v.setDStamp(clock++); v.setStatus(DISCOVERED); visit(v, info);//访问当前顶点 for (Iterator it = v.outEdges(); it.hasNext();) {//检查与顶点v Edge e = (Edge)it.getNext();//通过边e = (v, u) Vertex u = (Vertex)e.getVPosInV(1).getElem();//相联的每一顶点u switch (u.getStatus()) {//根据u当前的不同状态,分别做相应处理 case UNDISCOVERED ://若u尚未被发现,则 e.setType(TREE);//e被归类为“树边” traverse(u, info);//从u出发,继续做深度优先查找 break; case DISCOVERED ://若u已经被发现,但对其访问尚未结束,则 e.setType(BACKWARD);//将e归类为“后向跨边” break; default ://VISITED,即对u的访问已经结束 if (u.getDStamp() < v.getDStamp())//若相对于v,u被发现得更早,则 e.setType(CROSS);//将e归类为“横跨边” else//否则 e.setType(FORWARD);//将e归类为“前向跨边” break; } }//至此,v的所有邻居都已访问结束,故 v.setFStamp(clock++); v.setStatus(VISITED);//将v标记为VISITED return null;//然后回溯 } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值