AcWing 蓝桥杯AB组辅导课 01、递归与递推(一)

简介: AcWing 蓝桥杯AB组辅导课 01、递归与递推(一)

一、递归


知识点


递归,递归的深度决定了运算次数:



斐波那契的递归写法


例题


题目1:AcWing 95.斐波那契数列【简单,递归写法】


题目链接:21. 斐波那契数列


yxc总结—求解斐波那契数列的若干方法




class Solution {
    private int[] arr = new int[39];
    //1 1 2 3 5  f(i) = f(i - 1) + f(i - 2)   【i>2】
    //记忆化递归
    public int Fibonacci(int n) {
        if (n == 0) return 0;
        if (n == 1 || n == 2) return 1;
        int f1 = 0, f2 = 0;
        if (arr[n - 1] != 0) {
            f1 = arr[n - 1];
        }else {
            f1 = arr[n - 1] = Fibonacci(n - 1);
        }
        if (arr[n - 2] != 0) {
            f2 = arr[n - 2];
        }else {
            f2 = arr[n - 1] = Fibonacci(n - 2);
        }
        return f1 + f2;
    }
}


递归本质实际上是一棵递归搜索树。


题目2:AcWing 92.递归实现指数型枚举【简单】


链接:92. 递归实现指数型枚举


借助状态来进行枚举递归



import java.util.*;
class Main{
    private static int n;
    private static int[] arr;//0表示初始,1表示选,2表示不选
    public static void dfs(int u) {
        if (u == n) {
            for (int i = 0; i < n; i++) {
                if (arr[i] == 1) {
                    System.out.printf("%d ",i + 1);
                }
            }
            System.out.println();
            return;
        }
        //递归
        arr[u] = 1;
        dfs(u + 1);
        arr[u] = 0;
        arr[u] = 2;
        dfs(u + 1);
        arr[u] = 0;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        arr = new int[n];
        dfs(0);
    }
}




若是我们想要存储所有的路径呢?


①使用一个数组存储:



import java.util.*;
class Main{
    private static final int N = 16;
    private static int n;
    private static int[] arr = new int[N];//0表示初始,1表示选,2表示不选
    //存储所有的路径
    private static int[][] ways = new int[1 << 15][16];
    private static int cnt = 0;//路径方案
    public static void dfs(int u) {
        if (u == n) {
            //存储路径方案
            for (int i = 0; i < n; i++) {
                if (arr[i] == 1) {
                    ways[cnt][i] = i + 1;
                }
            }
            cnt++;
            return;
        }
        //递归
        arr[u] = 1;
        dfs(u + 1);
        arr[u] = 0;
        arr[u] = 2;
        dfs(u + 1);
        arr[u] = 0;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        dfs(0);
        //统一进行输出
        for (int i = 0; i < cnt; i++) {
            for (int j = 0; j < n; j++) {
                if (ways[i][j] != 0) {
                    System.out.printf("%d ", ways[i][j]);
                }
            }
            System.out.println();
        }
    }
}



②使用集合工具类:



import java.util.*;
class Main{
    private static final int N = 16;
    private static int n;
    private static int[] arr = new int[N];//0表示初始,1表示选,2表示不选
    //存储所有的路径
    private static List<List<Integer>> ways = new ArrayList<>();
    public static void dfs(int u) {
        if (u == n) {
            List<Integer> path = new ArrayList<>();
            for (int i = 0; i < N; i++) {
                if (arr[i] == 1) {
                    path.add(i + 1);
                }
            }
            ways.add(path);
            return;
        }
        //递归
        arr[u] = 1;
        dfs(u + 1);
        arr[u] = 0;
        arr[u] = 2;
        dfs(u + 1);
        arr[u] = 0;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        dfs(0);
        //统一进行输出
        for (int i = 0; i < ways.size(); i++) {
            List<Integer> path = ways.get(i);
            for (int j = 0; j < path.size(); j++) {
                System.out.printf("%d ", path.get(j));
            }
            System.out.println();
        }
    }
}


运行时间:



题目3:AcWing 94.递归实现排列型枚举【简单,全排列】


链接:94. 递归实现排列型枚举


思路:表示顺序的。



时间复杂度的证明如下:



对于全排列的时间复杂度为O(n.n!),其中还计算上了输出的时间复杂度。


Java解法(超时):


import java.util.*;
class Main {
    private static int N = 9;
    private static int n;
    //路径结果集
    private static int[] state = new int[N];
    //1-5路径是否访问过
    private static boolean[] visited = new boolean[N];
    //u表示的是当前的数字的长度
    public static void dfs (int u) {
        if (u == n) {
            //开始输出方案
            for (int i = 0; i < n; i++) {
                System.out.printf("%d ", state[i]);
            }
            System.out.println();
            return;
        }
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                //进行标记
                visited[i] = true;
                state[u] = i + 1;
                //递归
                dfs(u + 1);
                //恢复现场
                visited[i] = false;
                state[u] = 0;
            }
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        dfs(0);
    } 
}



C++解法(不超时):


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 9;
int n;
int state[N];
bool visited[N];
void dfs (int u) {
    if (u == n) {
        for (int i = 0; i < n; i++) {
            printf("%d ", state[i]);
        }
        printf("\n");
        return;
    }
    for (int i = 0; i < n; i++) {
        if (!visited[i]) {
            state[u] = i + 1;
            visited[i] = true;
            dfs(u + 1);
            state[u] = 0;
            visited[i] = false;
        }
    }
}
int main() {
    scanf("%d", &n);
    dfs(0);
    return 0;
}



习题



题目1:AcWing 93.递归实现组合型枚举【简单】


链接:93. 递归实现组合型枚举


思路:在之前递归实现排列型枚举上来进行改进,这里只不过是限制了数值的范围及长度,我们只需要在for遍历i的过程时修改指定的值即可。



Java解法:


import java.util.*;
class Main {
    private static final int N = 30;
    private static int n, m;
    //这里面每条结果集都是m个,所以这里无需使用visted数组来表示是否访问过
    private static int[] state = new int[N];
    public static void  dfs (int u, int start) {
        //优化剪枝
        if (u + (n - start) < m) return;
        if (u == m) {
            //遍历打印
            for (int i = 0; i < m; i++) {
                System.out.printf("%d ", state[i]);
            }
            System.out.println();
            return;
        }
        //根据起始位置开始
        for (int i = start; i < n; i++) {
            state[u] = i + 1;
            dfs(u + 1, i + 1);
            state[u] = 0;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        dfs(0, 0);
    }
}



优化之后:



C++解法:


#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
const int N = 30;
int n, m;
int state[N];
void dfs(int u, int start) {
    //优化剪枝
    if (u + (n - start) < m) return;
    if (u == m) {
        for (int i = 0; i < m; i++) {
            printf("%d ", state[i]);
        }
        printf("\n");
        return;
    }
    for (int i = start; i < n; i++) {
        state[u] = i + 1;
        dfs(u + 1, i + 1);
        state[u] = 0;
    }
}
int main() {
    scanf("%d%d", &n, &m);
    dfs (0, 0);
    return 0;
}



40倍是什么鬼?c++这么快!

优化:对于当前数+剩余的情况数<结果数量时提前进行结束



//提前剪枝(当前的各自数量+剩余的各自数量) < 总的组成数量
//简单说:即便把后面所有的数都选上,都不够m个,当前分支绝对无解
if (u + (n - start) < m) return;


时间提升了3倍左右:



相关文章
|
6月前
|
Java C语言 C++
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-449 递归输出数字三角形
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-449 递归输出数字三角形
76 0
|
存储 算法
蓝桥杯:递归 与 例题:斐波那契数列及优化与应用
蓝桥杯:递归 与 例题:斐波那契数列及优化与应用
80 0
蓝桥杯:递推 例题:数字三角型问题
蓝桥杯:递推 例题:数字三角型问题
62 0
|
4月前
【蓝桥杯】[递归]母牛的故事
蓝桥杯——[递归]母牛的故事
63 1
【蓝桥杯】[递归]母牛的故事
|
5月前
|
Java
蓝桥杯Java组暴力递归搜图
蓝桥杯Java组暴力递归搜图
33 4
|
6月前
[蓝桥杯 2017 省 AB] 分巧克力
[蓝桥杯 2017 省 AB] 分巧克力
66 0
|
6月前
|
Java C语言 C++
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-446 递归输出数字
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-446 递归输出数字
59 1
|
6月前
|
Java C语言 C++
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-150 6-1 递归求二项式系数值
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-150 6-1 递归求二项式系数值
65 1
|
算法
[蓝桥杯] 递归与递推习题训练
蓝桥杯比赛只有四十天左右啦,最近会按照模块更新一些有关蓝桥杯算法题。学习算法不仅仅是为了参见比赛,更是以后找工作的一个敲门砖。废话不多说,我们直接看题。
80 0
|
机器学习/深度学习 算法
蓝桥杯:递推算法 递归算法 例题:斐波纳契数列
蓝桥杯:递推算法 递归算法 例题:斐波纳契数列
67 0