蓝桥杯DFS&BFS专题
DFS
介绍
DFS是深度优先搜索算法,是一种用于图形搜索的算法。它的基本思想是:从初始状态出发,沿着最先发现的路径前进,直到最后可以到达目标状态为止
具体步骤如下:
- 从初始状态出发,沿着第一条路径向前搜索;
- 如果未到达终止状态,则继续沿着这条路径继续搜索;
- 如果已经到达终止状态,则回溯,沿着另一条路径继续搜索;
伪代码
void dfs()//参数用来表示状态
{
if(到达终点状态){
...//根据题意添加
return;
} ]
//可能剪枝
if(特殊状态)//剪枝
return ;
//重复过程
for(扩展方式){
//满足什么条件才能重复继续的操作
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
//标记;
dfs();
//回溯操作
(还原标记);
//是否还原标记根据题意
}
}
}
例题
排列数字
给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法,现在,请你按照字典序将所有的排列方法输出
输入格式
共一行,包含一个整数 n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围 : 1 ≤ n ≤ 7
输入样例:
3
输出样例:
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
AC代码
-
C/C++
#include <iostream> using namespace std; const int N = 10; int n; int path[N];//保存数字组合 bool st[N];//检测该数字有没有用过 void dfs(int x){ if (x==n){ for (int i = 0; i < n; i++){ cout << path[i]; } cout << endl; return; } for (int i = 1; i <= n; i++){ if (!st[i]){ path[x] = i; st[i] = true; dfs(x + 1); st[i] = false; } } } int main(){ cin >> n; dfs(0); return 0; }
-
JAVA
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static int n;
public static int [] vis = new int[10];
public static LinkedList<Integer> path = new LinkedList<>();
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static void main(String[] args) {
n = sc.nextInt();
dfs(0);
}
private static void dfs(int x) {
if (x == n){
for (Integer item : path) {
ps.print(item + " ");
}
ps.println();
return ;
}
for (int i = 1; i <= n; i++) {
if (vis[i] == 0){
vis[i] = 1;
path.add(i);
dfs(x + 1);
vis[i] = 0;
path.removeLast();
}
}
}
}
-
技巧
//其实就是一个全排列,可以调用对应函数或者API #include<iostream> #include<algorithm> using namespace std; int path[10]; int main(){ int n ; cin >> n; for(int i = 1 ; i <= n ; i++) path[i] = i; do{ for(int i = 1 ; i <= n ; i++){ cout << path[i]; } cout << endl; }while(next_permutation(path + 1,path + n + 1)); return 0; }
15级台阶
小蓝要上一个楼梯,共 15 级台阶。
小蓝每步可以上 1 级台阶,也可以上 2 级、3 级或 4 级,再多就没办法一步走到了。
每级台阶上有一个数值,可能正也可能负。每次走到一级台阶上,小蓝的得分就加上这级台阶上的数值。台阶上的数值依 次为: 1, 2, 1, 1, 1, 1, 5, 5, 4, -1, -1, -2, -3, -1, -9。
小蓝希望不超过 6 步走到台阶顶端,请问他得分的最大值是多少?
注意,小蓝开始站在地面上,地面没有分值。他最终要走到台阶顶端,所以最终一定会走到数值为 -9 的那级台阶,所以 -9
一定会加到得分里面。
AC代码
答案: 5
- C++
#include<iostream>
#include<algorithm>
using namespace std;
int score[16] = {0,1,2,1,1,1,1,5,5,4,-1,-1,-2,-3,-1,-9};
int ans;
void dfs(int x,int step,int grade) // 当前在 x 级台阶,用了step步,得了grade分
{
if(x > 15 || step > 6) return ;
if(x == 15) {
ans = max(ans,grade);
return ;
}
for(int i = 1 ; i <= 4 ; i++){
dfs(x + i,step + 1,grade + score[x + i]);
}
}
int main(){
dfs(0,0,0);
cout << ans << endl;
return 0;
}
- JAVA
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static int [] score = new int[]{0,1,2,1,1,1,1,5,5,4,-1,-1,-2,-3,-1,-9};
public static int ans;
public static void main(String[] args) {
dfs(0,0,0);
ps.println(ans);
}
private static void dfs(int x,int step,int grade) {
if (x == 15) {
ans = Math.max(ans,grade);
return ;
}
for (int i = 1; i <= 4; i++) {
if (x + i <= 15 && step <= 5) dfs(x + i,step + 1, grade + score[i + x]);
}
}
}
剪邮票
如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来,要求必须是连着的。(仅仅连接一个角不算相连)
比如,下图中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法
AC代码
技巧
选不重复的方法
从12个数中任意挑选5个数,用一个哈希表维护一个选取的数的记录,每次选取的数字都存入哈希表中,如果哈希表中已经存在该数字,则重新选择。
可以先将12个数排序,然后从第一个数开始,每次挑选下一个数字即可,保证不会重复。
-
JAVA
错误做法
import java.io.BufferedInputStream; import java.io.PrintStream; import java.util.LinkedList; import java.util.Scanner; public class Main { public static Scanner sc = new Scanner(new BufferedInputStream(System.in)); public static PrintStream ps = new PrintStream(System.out); public static LinkedList<Integer> path = new LinkedList<>(); public static int [] vis = new int[15]; public static int ans; public static void main(String[] args) { dfs(0,0); ps.println(ans); } private static void dfs(int x,int num) { if (num == 5){ if (Check()) ans++; return ; } for (int i = x + 1; i <= 12; i++) { if (vis[i] == 0){ vis[i] = 1; path.add(i); dfs(i,num + 1); vis[i] = 0; path.removeLast(); } } } private static boolean Check() { for (Integer item : path) { boolean flag = false; for (Integer item2 : path) { if (item == item2) continue; int abs = Math.abs(item - item2); if (abs == 1 || abs == 4){ //在举例,会发现bug flag = true; break; } } if (!flag) return false; } return true; } }
-
正解
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static Scanner sc = new Scanner(new BufferedInputStream(System.in));
public static PrintStream ps = new PrintStream(System.out);
public static LinkedList<Integer> path = new LinkedList<>();
public static int [] vis = new int[15];
public static int [] a = new int[] {
1,