目录
本章包括迷宫问题、Flood Fill 洪水灌溉问题、棋盘问题
第二题:[USACO10OCT]Lake Counting S
注意一种情况:当访问不到下一行的时候需要强行访问,如这种情况第二行永远访问不到(自行推理一下)
往篇同系列文章——传送门
【蓝桥杯】DFS深度优先练习题——基础入门模板(1)_小卢先冲的博客-CSDN博客第一题:递归实现指数型枚举、第二题:全排列问题、第三题:组合的输出https://blog.csdn.net/weixin_61082895/article/details/129874100?spm=1001.2014.3001.5501
【蓝桥杯】DFS正确入门方式 ——基础入门模板(2)_小卢先冲的博客-CSDN博客第一题:[NOIP2002 普及组] 选数 、第二题:烤鸡 、 第三题:[NOIP2004 普及组] 火星人第四题:[NOIP2008 提高组] 火柴棒等式、第五题:PERKET、第六题:奇怪的电梯https://blog.csdn.net/weixin_61082895/article/details/129895135?spm=1001.2014.3001.5501
DFS正确入门方式 | DFS + 递归与递推习题课(下) | 一节课教你爆搜!_哔哩哔哩_bilibili
大佬教学视频,非常细!
本章包括迷宫问题、Flood Fill 洪水灌溉问题、棋盘问题
第一题:入门
题目描述
不是任何人都可以进入桃花岛的,黄药师最讨厌像郭靖一样呆头呆脑的人。所以,他在桃花岛的唯一入口处修了一条小路,这条小路全部用正方形瓷砖铺设而成。有的瓷砖可以踩,我们认为是安全的,而有的瓷砖一踩上去就会有喷出要命的毒气,那你就死翘翘了,我们认为是不安全的。你只能从一块安全的瓷砖上走到与他相邻的四块瓷砖中的任何一个上,但它也必须是安全的才行。
由于你是黄蓉的朋友,她事先告诉你哪些砖是安全的、哪些砖是不安全的,并且她会指引你飞到第一块砖上(第一块砖可能在任意安全位置),现在她告诉你进入桃花岛的秘密就是:如果你能走过最多的瓷砖并且没有死,那么桃花岛的大门就会自动打开了,你就可以从当前位置直接飞进大门了。
注意:瓷砖可以重复走过,但不能重复计数。
输入格式
第一行两个正整数 W 和 H,分别表示小路的宽度和长度。
以下 H 行为一个 H×W 的字符矩阵。每一个字符代表一块瓷砖。其中,
.
代表安全的砖,#
代表不安全的砖,@
代表第一块砖。输出格式
输出一行,只包括一个数,即你从第一块砖开始所能安全走过的最多的砖块个数(包括第一块砖)。
输入输出样例
输入
11 9 .#......... .#.#######. .#.#.....#. .#.#.###.#. .#.#..@#.#. .#.#####.#. .#.......#. .#########. ...........输出 #1复制
59说明/提示
数据规模与约定
对于全部的测试点,保证 1≤W,H≤20。
迷宫问题
题目分析
难点
地图怎么存
static char arr[][];//地图怎么走,怎么拐弯
static int dx[] = {-1, 0, 1, 0};//上右下左 static int dy[] = {0, 1, 0, -1};- 边界是什么,怎么走'.'不走'#'
if (a < 0 || a >= n || b < 0 || b >= m) continue; if (arr[a][b] != '.') continue;怎么表示一个点有没有被走过
static Boolean st[][];//记录瓷砖走没走过if (st[a][b]) continue;//这个点被走过
题目代码
import java.util.Scanner; public class 入门_dfs { static int n, m, res; static Boolean st[][];//记录瓷砖走没走过 static char arr[][];//地图 static int dx[] = {-1, 0, 1, 0};//上右下左 static int dy[] = {0, 1, 0, -1}; public static void main(String[] args) { arr = new char[25][25]; st = new Boolean[25][25]; Scanner sca = new Scanner(System.in); m = sca.nextInt();//列 n = sca.nextInt();//行 sca.nextLine(); for (int i = 0; i < n; i++) {//输入 arr[i] = sca.next().toCharArray(); } int a = 0, b = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { st[i][j] = false; if (arr[i][j] == '@') { st[i][j] = true; a = i; b = j; } } } dfs(a,b); res++; System.out.println(res); } static void dfs(int x, int y) {//当前访问的坐标是 x,y for (int i = 0; i < 4; i++) {//拐弯 共4个方向 int a = x + dx[i], b = y + dy[i];//顺序为上右下左 if (a < 0 || a >= n || b < 0 || b >= m) continue; if (arr[a][b] != '.') continue; if (st[a][b]) continue;//这个点被走过 //走(a, b)这个点 st[a][b] = true; res++; dfs(a, b);//搜索 } } }
第二题:[USACO10OCT]Lake Counting S
题目描述
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个 N×M(1≤N≤100,1≤M≤100) 的网格图表示。每个网格中有水(
W
) 或是旱地(.
)。一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑。输入第 1 行:两个空格隔开的整数:N 和 M。
第 2 行到第N+1 行:每行 M 个字符,每个字符是
W
或.
,它们表示网格图中的一排。字符之间没有空格。输出一行,表示水坑的数量。
输入输出样例
输入 #1复制
10 12 W........WW. .WWW.....WWW ....WW...WW. .........WW. .........W.. ..W......W.. .W.W.....WW. W.W.W.....W. .W.W......W. ..W.......W.输出 #1复制
3
洪水灌溉问题
题目分析
难点
地图怎么存
static char mp[][] = new char[150][150];//地图怎么走,怎么拐弯
static int dx[] = {1, 1, 1, 0, 0, -1, -1, -1};//8连通 static int dy[] = {-1, 0, 1, 1, -1, 1, 0, -1};- 边界是什么
if (a < 0 || a >= n || b < 0 || b >= m) continue;//换一个方向需要灌溉的位置要满足什么条件
if (mp[i][j] == 'W' && !st[i][j]) {//灌溉 dfs(i, j); res++;if (mp[a][b] != 'W') continue;//这个位置不能灌溉,换一个方向 if (st[a][b]) continue;//这个位置被灌溉过了,换一个方向
题目代码
import java.util.Scanner; public class 洪水灌溉问题_dfs { static int n, m, res; static char mp[][] = new char[150][150];//地图 static Boolean st[][] = new Boolean[150][150];//表示每个位置有没有被灌溉过 static int dx[] = {1, 1, 1, 0, 0, -1, -1, -1};//8连通 static int dy[] = {-1, 0, 1, 1, -1, 1, 0, -1}; public static void main(String[] args) { Scanner sca = new Scanner(System.in); n = sca.nextInt(); m = sca.nextInt(); sca.nextLine(); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { st[i][j] = false; } } for (int i = 0; i < n; i++) { mp[i] = sca.next().toCharArray(); } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mp[i][j] == 'W' && !st[i][j]) { dfs(i, j); res++; } } } System.out.println(res); } static void dfs(int x, int y) {//给符合条件的位置浇水 for (int i = 0; i < 8; i++) {//8连通 int a = x + dx[i], b = y + dy[i]; if (a < 0 || a >= n || b < 0 || b >= m) continue;//换一个方向 if (mp[a][b] != 'W') continue;//这个位置不能灌溉,换一个方向 if (st[a][b]) continue;//这个位置被灌溉过了,换一个方向 st[a][b] = true; dfs(a, b); } } }
第三题:棋盘问题
题目描述
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。
要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放 k 个棋子的所有可行的摆放方案数目 C。
输入格式
输入含有多组测试数据。
每组数据的第一行是两个正整数 n,k,用一个空格隔开,表示了将在一个 n∗n 的矩阵内描述棋盘,以及摆放棋子的数目。当为
-1 -1
时表示输入结束。随后的 n 行描述了棋盘的形状:每行有 n 个字符,其中
#
表示棋盘区域,.
表示空白区域(数据保证不出现多余的空白行或者空白列)。输出格式
对于每一组数据,给出一行输出,输出摆放的方案数目 C (数据保证 C<231)。
数据范围
n≤8,k≤n
输入样例:
2 1 #. .# 4 4 ...# ..#. .#.. #... -1 -1
输出样例:
2 1
棋盘问题——二维版全排列模型
题目分析
难点
保证每行每列最多只放一个棋子
枚举每一行的每一列 即判断当前行的每一列有没有棋子static void dfs(int x, int count) //x表示枚举当前行,count表示放了几个棋子for (int i = 0; i < n; i++) //枚举列 if (!st[i] && map[x][i] == '#') //当这一列没有放棋子时棋子只能放 在'#’区域,不能放在 '.'区域
注意一种情况:当访问不到下一行的时候需要强行访问,如这种情况第二行永远访问不到(自行推理一下)
2 1 #. .#
dfs(x+1,count);//当访问不到下一行强行访问
题目代码
import java.util.Scanner; public class 棋盘问题_dfs { static int n, k, res; static boolean st[] = new boolean[10];//用来判断每一列是否放了元素 static char[][] map = new char[10][10];//地图 public static void main(String[] args) { Scanner sca = new Scanner(System.in); while (true) {//输入 n = sca.nextInt(); k = sca.nextInt(); if (n == -1 && k == -1) break; sca.nextLine(); for (int i = 0; i < n; i++) { map[i] = sca.next().toCharArray(); } res = 0; dfs(0, 0); System.out.println(res); } } static void dfs(int x, int count) {//x表示枚举当前行,count表示放了几个棋子 if (count == k) { res++; return; } if (x >= n) return; for (int i = 0; i < n; i++) {//枚举列 if (!st[i] && map[x][i] == '#') {//当这一列没有放棋子时 st[i] = true; dfs(x + 1, count + 1); st[i] = false;//回溯恢复现场 } } dfs(x+1,count);//当访问不到下一行强行访问 } }
第四题:[NOIP2001 提高组] 数的划分
题目描述
将整数 n 分成 k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,51,1,5;
1,5,11,5,1;
5,1,15,1,1.问有多少种不同的分法。
输入格式
n,k (6<n≤200,2≤k≤6)
输出格式
1 个整数,即不同的分法。
输入输出样例
输入 #1复制
7 3输出 #1复制
4说明/提示
四种分法为:
1,1,51,1,5;
1,2,41,2,4;
1,3,31,3,3;
2,2,32,2,3.
题目分析
dfs组合型模板——升级版(元素可重复选)
题目代码
import java.util.Scanner; public class 数的划分_dfs { static int n, k, res; static int arr[] = new int[250];//存答案 这道题不需要输出答案 public static void main(String[] args) { Scanner sca = new Scanner(System.in); n = sca.nextInt(); k = sca.nextInt(); dfs(1, 0, 1); System.out.println(res); } static void dfs(int x, int sum, int start) {//x:枚举的位置 sum:每次枚举到当前位置的和 start每次从几开始枚举 if (sum > n) return; if (x > k) { if (sum == n) { res++; } return; } for (int i = start; i <= n; i++) { arr[i] = i; dfs(x + 1, sum + i, i);//注意这里每一个位置在枚举的时候是从i开始的不要写成start arr[i] = 0;//恢复现场 } } }