本人简介:男
年龄:18
作者:那就叫我亮亮叭
专栏:关于C/C++那点破事
书接上文,上次我们讲到了深搜的思想与经典的迷宫问题,这次我们不再细节性讲解其思想,重在针通过几道例题来实践掌握深搜的用法,欢迎大佬们来指点一二,我们一起加油!!还没了解的可以直接点进行学习哦【C++算法】dfs深度优先搜索(上) ——【全面深度剖析+经典例题展示】
中国象棋博大精深,其中
马
的规则最为复杂,也是最难操控的一颗棋子。我们都知道象棋中马
走"日"
,比如在(2, 4)位置的一个马【如下图所示】,跳一步能到达的位置有(0,3),(0,5),(1,2),(1,6),(3,2),(3,6),(4,3),(4,5)。
蒜头君正在和花椰妹下棋,蒜头君正在进行战略布局,他需要把在(x, y)位置的马跳到(x’,
y’)位置,以达到威慑的目的。但是棋盘大小有限制,棋盘是一个10 x 9
的网格,左上角坐标为(0,0),右下角坐标为(9,
8),马不能走出棋盘
,并且有些地方已经有了棋子,马也不能跳到有棋子的点
。蒜头君想知道,在不移动其他棋子的情况下
,能否完成他的战略目标。
'.'
表示空位置
,用'#'
表示该位置有棋子
,用'S'
表示初始的马的位置
,用'T'
表示马需要跳到的位置
。'S'
和'T'
。如果在不移动其他棋子的情况下,马能从'S'
跳到'T'
,那么输出一行"Yes"
,否则输出一行"No"
。
.#....#S#
..#.#.#..
..##.#..#
......##.
...T.....
...#.#...
...#.....
...###...
.........
.##......
Yes
和上一篇例题的思路基本一模一样,只不过上次迷宫只能是上下左右走,而这次的象棋中可以走八个方向,只是对x、y坐标的改变不同而已,其他基本一致,所以这里就不过多赘述了,不太明白的可以点这里哦【C++算法】dfs深度优先搜索(上) ——【全面深度剖析+经典例题展示】
大致思路如下:
#include
#include
using namespace std;
int n, m;
char pos[105][105];
int trace[105][105];
int dir[8][2] = { {-1 , -2} , {-2 , -1} , {-2 , 1} , {-1 , 2} , {1 , 2} , {2 , 1} , {2 , -1} , {1 , -2} }; // 找出马儿走"日"时x、y坐标变化的所有情况【基本按逆时针来】
bool check_in(int x, int y) { // 判断数组是否越界
return (x >= 1 && x <= n && y >= 1 && y <= m); //这里表示的是如果()里为真,则返回true,否则返回false
}
bool dfs(int x, int y) {
if (pos[x][y] == 'T') {
return true;
}
pos[x][y] = 'x'; //用x记录路径
trace[x][y] = 1; //标记已走过
for (int i = 0; i < 8; i++) {
int xx = x + dir[i][0]; //表示x加上第几个方向的第1个数,即变化后行的坐标
int yy = y + dir[i][1]; //表示y加上第几个方向的第2个数,即变化后列的坐标
if (pos[xx][yy] != '#' && check_in(xx, yy) && !trace[xx][yy]) {
if (dfs(xx , yy)) {
return true;
}
}
}
pos[x][y] = '.'; // 如果一个位置的上下左右都走不了,则取消该位置的路径进行回溯,回溯过程把之前已标记的'x'改回'.'
trace[x][y] = 0; //走不通,则回溯过程标记为没走过
return false;
}
int main() {
cin >> n >> m;
//输入象棋地图 找儿的起点
int x , y;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> pos[i][j];
if (pos[i][j] == 'S') {
x = i, y = j;
}
}
}
cout << "--------------------------------" << endl;
if (dfs(x , y)) {
cout << "Yes!!!" << endl;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cout << pos[i][j];
} cout << endl;
}
}
else {
cout << "No!!!" << endl;
}
return 0;
}
蒜头君和他的朋友周末相约去召唤师峡谷踏青。他们发现召唤师峡谷的地图是由
一块一块格子
组成的,有的格子上是草丛,有的是空地。草丛通过上下左右4个方向
扩展其他草丛形成一片草地
,任何一片草地中的格子都是草丛,并且所有格子之间都能通过上下左右连通。如果用'#"
代表草丛
,'.'
代表空地
,则下面的峡谷中有2片草地。
##..
..##
同一
个草地的2个人可以相互看到
,空地看不到草地里面的人。他们发现有一个朋友不见了,现在需要分头去找,每个人负责一片草地
,蒜头君想知道他们至少需要多少人
。5 6
.#....
..#...
..#..#
...##.
.#....
5
最少人数
,而根据一人负责一片草地
,则就转换求最少草地数
,就是该地形最少有多少片土地。我们先输入地形,再依次遍历每个格子是否为草地,如果不是草地,直接略过;如果是草地,则是否之前找过,如果找过则还是略过,如果没找过,则先记录下已找过,结果+1,并对该草地的上下左右遍历是否是未被找的草地【深搜思想】。最后输出结果。#include
#include
using namespace std;
int n, m, ans = 0;
char pos[105][105];
bool trace[105][105];
//法一
//bool check_in(int x, int y) {
// return (x >= 1 && x <= n && y >= 1 && y <= m);
//}
//
//void dfs(int x, int y) {
// if (check_in(x, y) && pos[x][y] == '#' && !trace[x][y]) {
// trace[x][y] = 1;
// dfs(x, y + 1);
// dfs(x + 1, y);
// dfs(x, y - 1);
// dfs(x - 1, y);
// }
//}
//法二
void dfs(int x, int y) {
if (x < 1 || x > n || y < 1 || y > m || trace[x][y] || pos[x][y] == '.') {
return;
}
trace[x][y] = true;
dfs(x - 1, y); //记录上面
dfs(x , y - 1); //记录左面
dfs(x + 1, y); // 记录下面
dfs(x , y + 1); //记录右面
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> pos[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (!trace[i][j] && pos[i][j] == '#') { // 假如是草丛的话且之前没找过
dfs(i, j); // 把这个位置和周边草丛标记好
ans++; //记录草丛数量
}
}
}
cout << ans << endl;
return 0;
}
蒜头君是一个玩迷宫的高手,天下还没有能难住他的迷宫。但是总有人喜欢习难蒜头君,不停的给蒜头君出难题。这个出题的人很聪明,他知道天下还没有能难住蒜头君的迷宫。
不走重复路径
的情况下,总共有多少不同可以到达终点的路径
呢?蒜头君稍加思索便给出了答案,你要不要也来挑战一下?n
(1≤n≤11), m
(1≤m≤11), 表示迷宫的行
和列
。然后有一个n*m
的地图,地图由'.'
、'#'
、 's'
、'e'
这四个部分组成。'.'
表示可以通行的路
,'#'
表示迷宫的墙
,'s'
表示起始点
,'e'
表示终点
。's'
到达'e'
的所有方案数。5 5
s####
.####
.####
.####
....e
1
清空路径
,trace[x][y]=0
,来继续探索下一条路。#include
#include
using namespace std;
int n, m, ans = 0;
char pos[105][105];
bool trace[105][105];
int dir[4][2] = { {-1, 0},{0,-1},{1,0},{0,1} };
bool check_in(int x, int y) {
return (x >= 1 && x <= n && y >= 1 && y <= m);
}
void dfs(int x, int y) {
if (pos[x][y] == 'e') {
ans++; //节点为终点,所以找到一条走到终点的路,ans++;
return ;
}
if (check_in(x, y) && pos[x][y] != '#' && !trace[x][y]) { //如果这个节点不是终点且没越界,不是墙且没走过,则统计四周情况
trace[x][y] = 1;
for (int i = 0; i < 4; i++) {
int xx = x + dir[i][0];
int yy = y + dir[i][1];
dfs(xx, yy);
}
trace[x][y] = 0; //清空路线,找下一条路
}
}
int main() {
cin >> n >> m;
int x, y;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> pos[i][j];
if (pos[i][j] == 's') {
x = i, y = j;
}
}
}
dfs(x, y);
cout << ans << endl;
return 0;
}
这一天蒜头君生日,他的朋友们一起来给蒜头君买一个大的蛋糕过生日。游戏做完后到了切蛋糕的时刻了,朋友们知道蒜头君喜欢吃蛋糕,便让蒜头君自己给自己切-块最大的。蒜头君看朋友们这么热情也就不客气了。
R*C
的网格构成,每个网格上面都放有不同的水果。蒜头君把这些水果分为两类,一类是自己喜欢吃的水果
,用'#'
来表示;一类是自己不喜欢吃的水果
,用'.'
来表示。R(1≤R≤1000)和C(1≤C≤1000)
。然后会有一个R*C
的网格,由'#'
和'.'
组成。5 6
.#....
..#...
..#..#
...##.
.#....
2
和题2《踏青》的总体思路一样,但《踏青》求的是带'#'
每部分的数量,而本题是求每部分里'#'
最多的数量。所以这道题只需要在统计每部分时记录'#'
的数量再进行比较
即可。
#include
#include
using namespace std;
int r, c, ans = 0, cnt = 0;
char pos[105][105];
bool trace[105][105];
int dir[4][2] = { {-1, 0},{0,-1},{1,0},{0,1} };
bool check_in(int x, int y) {
return (x >= 1 && x <= r && y >= 1 && y <= c);
}
void dfs(int x, int y) {
if (!check_in(x, y) || pos[x][y] != '#' || trace[x][y]) { //深搜过程中不合要求的返回;
return;
}
trace[x][y] = 1; //标记访问过
cnt++; //记录数量
for (int i = 0; i < r; i++) {
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if (pos[xx][yy] == '#') {
dfs(xx, yy);
}
}
}
int main() {
cin >> r >> c;
int x, y;
for (int i = 1; i <= r; i++) {
for (int j = 1; j <= c; j++) {
cin >> pos[i][j];
}
}
for (int i = 1; i <= r; i++) {
for (int j = 1; j <= c; j++) {
if (!trace[i][j] && pos[i][j] == '#') { //如果是'#',且之前没被访问过,则用dfs记录下
cnt = 0; //需要将该位置能求到的数量重置为0
dfs(i, j); //用dfs求一下所有‘#’的数量
if (ans < cnt) {
ans = cnt; //更新一下ans
}
}
}
}
cout << ans << endl;
return 0;
}
家谱,又称族谱、宗谱等,是一种以表谱形式,记载一个家族的世系繁衍及重要人物事迹的书。皇帝的家谱称玉牒,如新朝玉牒、皇宋玉牒。它以记载父系家族世系、人物为中心,由正史中的帝王本纪及王侯列传、年表等演变而来。家谱是一种特殊的文献,就其内容而言,是中国五千年文明史中具有平民特色的文献,记载的是同宗共祖血缘集团世系人物和事迹等方面情况的历史图籍。家谱属珍贵的人文资料,对于历史学、民俗学、人口学、社会学和经济学的深入研究,均有其不可替代的独特功能。
直系后代包括他的孩子和他孩子的直系后代
)。但是家族历史源远流长,家谱实在太庞大了,自己一个人完全数不过来。热心的你便自告奋勇帮蒜头君写一个程序,来统计每位祖先有多少直系后代
。n
(1n- 1
行,每行有两个整数x
(1≤x≤n),y
(1≤y≤n),表示x是y的父母
。4
1 2
1 3
2 4
3
1
0
0
本题输入数值的大小不确定,所以如果指定数组大小,则可能报错,所以我们采用动态数组vector来统计son数量。所以首先,我们先找根节点
,所以要找直系儿子,则需要把所有儿子的后代标记一下
,最后未被标记
的就是直系儿子
。我们找到根节点,需要先对根节点的后代进行搜索,直到找到答案。
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
int n, x, y, u;
vector<int> son[N]; //使用动态数组,便于插入和删除
bool judge_son[N]; //判断是否是儿子
int ans[N];
int dfs(int a) { // 找有多少个直系后代【不包括自己】,但统计时为了方便,则统计自己
int cnt = 0;
int len = son[a].size();
for (int i = 0; i < len; i++) { //首先,找根节点的后代
cnt += dfs(son[a].at(i)); //找根节点的直接后代,或者写dfs(son[u][i])
}
ans[u] = cnt; //统计后代个数
return cnt + 1; //加上自己
}
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
cin >> x >> y; //输入x,y 表示x是y的父亲
son[x].push_back(y); //将y放入son[x]的动态数组中
judge_son[y] = true; //y已经作为儿子了,所以标为true;
}
for (int i = 1; i <= n; i++) { //找起始位置,即找“根”节点
if (!judge_son[i]) { //找到赋给u,并break
u = i;
break;
}
}
dfs(u);
for (int i = 1; i <= n; i++) {
cout << ans[i] << endl;
}
return 0;
}
如果感觉这篇文章对你有帮助的话,不妨三连支持下,十分感谢(✪ω✪)。
printf("点个赞吧*^*");
cout << "收藏一下叭o_o";
System.out.println("评论一下吧^_^");
print("关注一下叭0-0")