文章目录

  • 前言
  • 一、递归
    • 知识点
    • 例题
      • 题目1:AcWing 95.斐波那契数列【简单,递归写法】
      • 题目2:AcWing 92.递归实现指数型枚举【简单】
      • 题目3:AcWing 94.递归实现排列型枚举【简单,全排列】
    • 习题
      • 题目1:AcWing 93.递归实现组合型枚举【简单】
      • 题目2:AcWing 1209.带分数【简单,蓝桥杯编程第2题】
      • 题目3:AcWing 95.费解的开关【中等】
      • 题目4:AcWing 116.飞行员兄弟【简单】
      • 题目5:AcWing 1208.翻硬币【简单,蓝桥杯2013年B组第8题】
  • 二、递推
    • 例题
      • 题目1:AcWing 95.斐波那契【简单,递推写法】

前言

前段时间为了在面试中能够应对一些算法题走上了刷题之路,大多数都是在力扣平台刷,目前是300+,再加上到了新学校之后,了解到学校也有组织蓝桥杯相关的程序竞赛,打算再次尝试一下,就想系统学习一下算法(再此之前是主后端工程为主,算法了解不多刷过一小段时间),前段时间也是第一次访问acwing这个平台,感觉上面课程也是比较系统,平台上题量也很多,就打算跟着acwing的课程来走一段路,大家一起共勉加油!

  • 目前是打算参加Java组,所以所有的题解都是Java。

本章节递归与递推习题一览:包含所有题目的Java题解链接

例题:

  • AcWing 92. 递归实现指数型枚举 Java题解
  • AcWing 94. 递归实现排列型枚举 Java & C++
  • AcWing 21. 斐波那契数列 Java题解 (递归、递推写法)
  • AcWing 95. 费解的开关 Java题解

习题:

  • AcWing 21. 斐波那契数列 Java题解 (递归、递推写法)
  • AcWing 1209. 带分数 Java题解
  • AcWing 116. 飞行员兄弟 Java题解
  • AcWing 1208. 翻硬币 Java题解

一、递归

知识点

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

斐波那契的递归写法

例题

题目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倍左右:


题目2:AcWing 1209.带分数【简单,蓝桥杯编程第2题】

来源:第四届蓝桥杯省赛C++B/C组,第四届蓝桥杯省赛JAVAA/B组

链接:1209. 带分数

import java.util.*;class Main {private static int n;private static boolean[] v = new boolean[10];//记录结果集private static int ans;//检查private static boolean check(int a, int c) {//计算bint b = n * c - a * c;//提前剪枝(若是出现0则直接结束)if (a == 0 || b <= 0 || c == 0) return false;//复制拷贝是否存在的数组(避免重新恢复现场)boolean[] vv = Arrays.copyOfRange(v, 0, v.length);//遍历b的所有元素while (b != 0) {int x = b % 10;//若是b中出现0或者之前已经出现过,此时直接就结束if (x == 0 || vv[x]) return false;//记录访问过vv[x] = true;b = b / 10;}//遍历10个数是否都有存在for (int i = 1; i <= 9; i++) {if (!vv[i]) return false; }return true;}public static void dfs_c(int u, int a, int c) {//c以n的最大长度最为基准if (u == n) return;//相当于枚举出来一组a,cif (check(a, c)) ans++;//向下递归推举出下一个cfor (int i = 1; i <= 9; i++) {if (!v[i]) {v[i] = true;dfs_c(u + 1, a, c * 10 + i);v[i] = false;}}}//在进行递归推的时候算出a的值public static void dfs_a(int u, int a) {//若是a的值>=n数字,此时就直接提前结束if (a >= n) return;//符合条件情况,向下递归推出cif (a != 0) {dfs_c(u, a, 0);}for (int i = 1; i <= 9; i++) {if (!v[i]) {v[i] = true;dfs_a(u + 1, a * 10 + i);v[i] = false;}}}//枚举出来a,c => 推出b,最后进行check检查//全排列+check检查【更加复杂】public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();dfs_a(0, 0);System.out.printf("%d", ans);}
}

题目3:AcWing 95.费解的开关【中等】

来源:《算法竞赛进阶指南》

链接:95. 费解的开关

  • 学习链接:AcWing 95. 费解的开关

思路:是否点亮全部的灯泡完全取决于第一行是否按灯泡开关起到了决定性的作用,后面的四行都是依据前一行来进行开关动作。当前给出的是固定范围,5x5,第一行有五个格子,换成二进制表示5位就有32中按法,该题中我们需要来进行所有方案的枚举即可找到最少次数的按键。

复杂度分析:时间复杂度计算(32x(25x5)x500)

  • 其中32表示一个问题解的第一行开关按钮的方案数,25表示要对25个格子进行遍历判断亮关,5表示若是进行开那么上下左右以及当前位置需要进行变换操作,最后的500则是可能出现最大500个方案问题。
  • 约等于2000000,在一亿次运行范围中。
import java.util.*;
class Main {//输入情况数private static int n;//backup表示备份矩阵private static int[][] arr = new int[5][5], backup = new int[5][5];//结果集private static int ans = Integer.MAX_VALUE;//定义x、y方向  上下左右中private static int[] dx = {-1, 1, 0, 0, 0};private static int[] dy = {0, 0, -1, 1, 0};//按开关public static void turn(int i, int j) {//五个位置:上下左右中for (int pos = 0; pos < 5; pos++) {int x = i + dx[pos];int y = j + dy[pos];//若是越界则不进行开关操作(1->0   0->)if (x < 0 || x >= 5 || y < 0 || y >= 5) continue;//0^1=1  1^1=0backup[x][y] ^= 1;}}/*0111111111111111111111111**/public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();while (n != 0) {//初始化for (int i = 0; i < 5; i++) {String s = sc.next();for (int j = 0; j < 5; j++) {arr[i][j] = s.charAt(j) - '0';}}//每个位置的开关操作表示一个按法(按or不按)//对于00000,5位可以有32种按法 00000 00001  00010  00011,其中1就是开,0就是不按for (int op = 0; op < 1 << 5; op++) {int cnt = 0;//记录当前方案按的次数//备份与arr同样的状态backup,之后的模拟操作针对backup数组for (int i = 0; i < 5; i++)for (int j = 0; j < 5; j++) backup[i][j] = arr[i][j];//对第一行进行操作//op表示的就是一个方案,根据来操作//根据当前的op方案来按第一行灯泡for (int j = 0; j < 5; j++){//取出最后一个,例如:0  二进制表示00000000,op>>0表示进行右移1位,此时&1,就是取到最后一位,当前是0//之后四个方案一次对应表示if ((op >> j & 1) == 1){turn(0, j);cnt++;}}//对后四行来进行操作for (int i = 0; i < 4; i++) {for (int j = 0; j < 5; j++) {//若是当前行没有点亮if (backup[i][j] == 0) {turn(i + 1, j);cnt++;}}}//check最后一行是否全部点亮(有一个没点亮表示该方案不成立)int j = 0;while (j < 5) {if (backup[4][j] == 0) break;j++;}//若是当前的方案能够完全点亮if (j == 5 && cnt <= 6) ans = Math.min(cnt, ans); }if (ans > 6) ans = -1;System.out.printf("%d\n", ans);n--;ans = Integer.MAX_VALUE;}}
}

题目4:AcWing 116.飞行员兄弟【简单】

来源:《算法竞赛进阶指南》

视频讲解:2.1

链接:116. 飞行员兄弟

y总分析:数据量小,可以使用暴力搜索。216=65535,数据量不大,枚举所有方案,再最终check所有状态。【太强了 】

优化:可以使用二进制来进行优化,不过当前使用数组方案是能够满足情况的。

思路:将16个格子从左往右,从上至下,看做是16位的数,可使用一个int整型表示。

import java.util.*;//顺序从上到下,从左到右
//枚举16位
//2^16 x 7 x 16 约733万
class Main {static class Pair<K, V> {K x;V y;public Pair(K x, V y) {this.x = x;this.y = y;}}static final int N = 4;static char[][] arr = new char[4][4], backup = new char[4][4];//保存结果集、按下的位置static List<Pair<Integer, Integer>> ansList;//根据i与j取得当前的位数public static int getPos(int i, int j) {return i * 4 + j;}public static void turnOne(int x, int y) {if (backup[x][y] == '-') backup[x][y] = '+';else backup[x][y] = '-';}//按住当前位置public static void turnAll(int i, int j) {for (int pos = 0; pos < 4; pos++) {turnOne(i, pos);turnOne(pos, j);}turnOne(i, j);}public static void main(String[] args) {Scanner sc = new Scanner(System.in);//初始化for (int i = 0; i < 4; i++) {String line = sc.next();for (int j = 0; j < 4; j++) {arr[i][j] = line.charAt(j);}}//定义不同的方案数for (int op = 0; op < 1 << 16; op++) {List<Pair<Integer, Integer>> list = new ArrayList<>();//根据指定方案来进行推算//备份一份状态for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {backup[i][j] = arr[i][j];}}//对backup来进行操作 for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {//当前方案该i,j位置需要进行按住if ((op >> getPos(i, j) & 1) == 1) {turnAll(i, j);list.add(new Pair<Integer, Integer>(i, j));}}}//最终进行check检查boolean flag = true;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (backup[i][j] != '-') {flag = false;break;}}}//报错结果集if (flag && (ansList == null || ansList.size() > list.size())) {ansList = list;}}//最终输出结果集System.out.printf("%d\n", ansList.size());for (int i = 0; i < ansList.size(); i++) {System.out.printf("%d %d\n", ansList.get(i).x + 1, ansList.get(i).y + 1);}}
}

题目5:AcWing 1208.翻硬币【简单,蓝桥杯2013年B组第8题】

来源:第四届蓝桥杯省赛C++B组

标签:递推

视频讲解:2.1中

链接:1208. 翻硬币

分析:两大类做法:初始状态->目标状态。

  • 可采用宽搜bfs,可使用时间复杂度比较高。只有状态数局面不大采用bfs。
  • 若是局面特别大可采用其他做法。

把其想象为开关的做法【此类就是一个模拟的问题】

有解必然只有一组唯一解。(看起来最少需要多少步,实际上只有一组解)

若是一次翻转3或4次时,也同样可以使用上面的模拟解决了【前面一次能够推举出后面按的操作】。

  • 10个字符,一次连续按两个,相当于9次,那么一个个依次去进行比较即可。
import java.util.*;class Main {public static void turn(int i) {from[i] = from[i] == '*' ? 'o' : '*';}static char[] from;static char[] to;public static void main(String[] args) {Scanner cin = new Scanner(System.in);from = cin.next().toCharArray();to = cin.next().toCharArray();//n - 1步int n = from.length;int ans = 0;for (int i = 0; i < n - 1; i++) {if (from[i] != to[i]) {//进行反转连续的两个turn(i);turn(i + 1);ans++;}}System.out.printf("%d", ans);}
}

二、递推

例题

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

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

class Solution {private int[] fn = new int[40];//递推public int Fibonacci(int n) {fn[1] = 1;fn[2] = 1;for (int i = 3; i <= n; i++) fn[i] = fn[i - 1] + fn[i - 2];return fn[n];}
}

AcWing 蓝桥杯AB组辅导课 01、递归与递推相关推荐

  1. AcWing蓝桥杯AB组辅导课10、疑难杂题

    文章目录 前言 例题1:AcWing 1242. 修改数组(并查集) 分析 题解:单链表式并查集 例题2:AcWing 1234. 倍数问题(背包问题+贪心) 分析 题解1:01背包问题,三维解法(贪 ...

  2. AcWing蓝桥杯AB组辅导课07、贪心

    文章目录 前言 一.贪心 模板题 例题1:AcWing 104. 货仓选址(贪心,简单,算法竞赛进阶指南) 分析 题解:贪心思路 例题 例题1:AcWing 1055. 股票买卖 II(贪心.状态机, ...

  3. AcWing蓝桥杯AB组辅导课08、数论

    文章目录 前言 一.数论 例题 例题1:AcWing 1246. 等差数列(最大公约数,第十届蓝桥杯省赛C++B第7题) 分析 题解:最大公约数 例题2:AcWing 1295. X的因子链(算数基本 ...

  4. AcWing 蓝桥杯AB组辅导课 05、树状数组与线段树

    文章目录 前言 一.树状数组 1.1.树状数组知识点 1.2.树状数组代码模板 模板题:AcWing 1264. 动态求连续区间和 例题 例题1.AcWing 1265. 数星星[中等,信息学奥赛一本 ...

  5. 2019蓝桥杯A组:数列求值(递推式)

    三.题目:数列求值 给定数列 1, 1, 1, 3, 5, 9, 17, -,从第 4 项开始,每项都是前 3 项的和.求 第 20190324 项的最后 4 位数字. [答案提交] 这是一道结果填空 ...

  6. 【AcWing】蓝桥杯C++ AB组辅导课

    蓝桥杯 C++ AB 组辅导课 教学计划与递归 如何准备蓝桥杯比赛 做题的套路 第一讲 递归与递推 递归实现指数型枚举 递归实现排列型枚举 递归实现组合型枚举 AcWing116.飞行员兄弟 第二讲 ...

  7. 蓝桥杯C++ AB组辅导课

    整理的算法模板合集: ACM模板 今天在AcWing闲逛白嫖到了yxc老师的蓝桥杯C++ AB组辅导课的题单,正好快要蓝桥杯了,我准备每天花半个小时刷5道这个题单里的水题,练一练,不然到时候我各种花里 ...

  8. 蓝桥杯C++ AB组辅导课 第一讲 递归与递推 Acwing

    例题 AcWing 92. 递归实现指数型枚举 从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案. 输入格式 输入一个整数 n. 输出格式 每行输出一种方案. 同一行内的数必须升序 ...

  9. 蓝桥杯C/C++ AB组辅导课

    文章目录 注意 algorithm 字符串 剪枝 暴力常识 1S时间复杂度 数论 暴力流打法 二进制巧用 归并排序 并查集(查找图中的环) 线段树 大数 国赛准备注意事项 还愿 后记 注意 注意数据范 ...

最新文章

  1. 2012-08-17
  2. 游戏行业全场景数字化解决方案全面上线,速来围观
  3. java静态方法声明_方法本地类中的Java最终静态声明
  4. linux redis release.c:37:10: fatal error: release.h: No such file or directory
  5. 交付效率提升40%,珍爱网基于微服务的DevOps落地指南
  6. 第十一期:数据挖掘其实就是为了干这四种事?
  7. 解决Uncaught (in promise) reason的问题
  8. python数据容器专题
  9. 云南昭通暴雨强度公式_昆明市暴雨强度公式推算与对比
  10. 常用软件分类运维或个人收藏软件必备,及文件夹打包下载
  11. 《现代操作系统》阅读笔记
  12. 进程的同步和互斥反映了_用信号量解决进程的同步与互斥探讨【持续更新】
  13. 帮表弟的女友买了个5900的dell 1420
  14. 【LuoguP4275】萃香的请柬-数学证明
  15. 通过windows官网工具制作win10启动盘并安装win10系统
  16. GLUT教程 - glutPostRedisplay函数
  17. Android 检测辅助功能是否开启,并调整设置页面
  18. 中国天气网天气预报API接口城市代码,XML格式,数据具体到县、区级别
  19. 区块链安全是场持久战,比特安审计与诸君共勉
  20. 小白安装Discuz!Q教程,学不会你过来打我!

热门文章

  1. Linux取ip地址
  2. 公众号测试号 进行H5授权获取code《前端部分》
  3. R绘图(5): 一文学会桑基图的画法
  4. 一篇文章了解html
  5. 集群聊天服务器项目(四)——项目总结
  6. 【 Apollo 】dreamview 无法启动
  7. 二维码登录(二)生成二维码
  8. 2021-4-28华为勇敢星实习笔试记录
  9. 人体口罩佩戴检测实战
  10. Java权限降级_简单值控制权限系统的改进