深度优先搜索
1.判断点是否能到终点
int main()
{
将所有点标记为新点;
起点 = x;
终点 = y;
cout << dfs(起点);
}
bool dfs(V)
{
//边界条件
if(v是终点)
return true;
if(v是旧点)
return false;
将v标记为旧点 表示要访问
for(跟v相邻的所有的点u)
{
//返回true表示u能到终点 所以v也可以到终点
if(dfs(u) == true)
return true;
}
//一直到循环了所有的相邻的点 不能到达终点 所以v也不能到
return false;
}
2.记录路径
//要记录路径,可以开一个数组,路径最长就是点的个数。
Node path[MAX_LEN];
int depth; //表示深度,也就是路径长度,初始为0
int main()
{
将所有点都标记为新点
depth = 0;
if(dfs(起点) == true)
{
//从0到depth,path[i]就是路径上第i步的点
for(int i = 0;i <= depth; i++)
{
cout<<path[i]<<endl;
}
}
}
bool dfs(v)
{
//边界条件
if(v为终点)
{
path[depth] = v;
return true;
}
if(v是旧点)
return false;
将v标记为已经访问过
path[depth] = v; //尝试把v放入到path
depth++; //深度应该加1
for(与v相邻的所有点u)
{
if(dfs(u) == true)
return true;
}
//如果u到不了终点 那么说明路径上v不合适
depth--; //直接将depth--即可,下一次更新path数组时直接覆盖
return false;
}
3.遍历所有的点(不连通也可以)
int main()
{
将所有点标记为新点
while(图中能找到v是新点)
{
dfs(v);
}
}
void dfs(v)
{
if(v是旧点)
return ;
将v标记为旧点
for(v相邻的所有的点u)
{
dfs(u);
}
}
例题练习
题目1:城堡问题 OpenJ_Bailian - 2815
描述:
1 2 3 4 5 6 7
#############################
1 # | # | # | | #
#####---#####---#---#####---#
2 # # | # # # # #
#---#####---#####---#####---#
3 # | | # # # # #
#---#########---#####---#---#
4 # # | | | | # #
#############################
(图 1)
# = Wall
| = No wall
- = No wall
图1是一个城堡的地形图。请你编写一个程序,计算城堡一共有多少房间,最大的房间有多大。城堡被分>割成mn(m≤50,n≤50)个方块,每个方块可以有0~4面墙。
输入:
程序从标准输入设备读入数据。第一行是两个整数,分别是南北向、东西向的方块数。在接下来的输入行里,每个方块用一个数字(0≤p≤50)描述。用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。城堡的内墙被计算两次,方块(1,1)的南墙同时也是方块(2,1)的北墙。输入的数据保证城堡至少有两个房间。输出:
城堡的房间数、城堡中最大房间所包括的方块数。结果显示在标准输出设备上。样例输入:
4
7
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13
样例输出:
5
9
思路分析:
首先由题目可以知道,抽象为模型,一个点可以到另一个点那么这两个点之间就有一条边,那么一个房间就是一个连通子图;而这道题求得就是最大连通子图的个数,以及其中点最多的最大连通子图的顶点个数。
其次,题目中说用一个数字表示方块周围的墙,1表示西墙,2表示北墙,4表示东墙,8表示南墙。每个方块用代表其周围墙的数字之和表示。我们知道1,2,4,8的二进制表示分别是0001,0010,0100,1000.也就是说这些数字的和(有哪些墙的组合)有一个规律:相应位置上的二进制为1,那么就表示有对应的方向的墙。比如13这个数字的二进制是1101,那么就表示有西墙、东墙和南墙。那么我们用 & 就可以看是否有对应的墙。
知道哪些方向有墙后,就能遍历整个图,那么我们用染色的思想,一个联通子图就染成一个颜色(用数字表示),最后就可以通过看有几种颜色,每种颜色有多少种来得到答案了。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int x,y; //x,y表示南北向,东西向的方块数
int map[55][55]; //用map存放城堡的地图
int color[55][55]; //用color数组来表示每个点是否访问过以及分组
int area,maxArea = 0; //表示当前子图的面积以及最大的面积
int num = 0; //房间数
void dfs(int i,int j)
{
//若已经访问过
if(color[i][j] != 0)
return;
area++; //该房间可走且未走过,所以面积++
color[i][j] = num; //让改点的color为num房间号,染色
//然后访问与i,j相邻的所有可以访问点
if((map[i][j] & 1) == 0) dfs(i,j-1); //西边无墙
if((map[i][j] & 2) == 0) dfs(i-1,j); //北边无墙
if((map[i][j] & 4) == 0) dfs(i,j+1); //东边无墙
if((map[i][j] & 8) == 0) dfs(i+1,j); //南边无墙
}
int main()
{
scanf("%d %d",&x,&y);
for(int i = 0;i < x; i++)
for(int j = 0;j < y; j++)
scanf("%d",&map[i][j]);
memset(color,0,sizeof(color)); //标记所有点为新点
for(int i = 0;i < x; i++)
{
for(int j = 0;j < y; j++)
{
if(color[i][j] == 0)
{
/*当该点未被访问过,那么说明一定是一个新的联通子图,
房间数++;*/
num ++;
area = 0; //当前房间面积设置为0
dfs(i,j);
if(maxArea<area)
maxArea = area;
}
}
}
//遍历完图,房间数和最大面积也就得出
printf("%d\n",num);
printf("%d\n",maxArea);
return 0;
}
例题练习
题目2:踩方格 OpenJ_Bailian - 4103
描述:
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b. 走过的格子立即塌陷无法再走第二次;
c. 只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。输入:
允许在方格上行走的步数n(n <= 20)输出:
计算出的方案数量样例输入:
2
样例输出:
7
思路分析:
首先根据题目,我们可以得出是要我们求出第i,j个位置走n步有多少种走法,其中只能向三个方向走
然后其实这个题也是用到了深度优先搜索,首先我们的起始状态就是(X0,Y0,N),表示我们在第X0行第X0列上,还要走n步的不同走法。我们的结束状态是(Xi,Yi,0),也就是到了某一个位置,还要走0步。我们要求得就是从初始状态到结束状态总共有多少种方法,其中每一个状态(Xi,Yi,Ni)就代表一个点。
而(Xi, Yi, Ni) = (Xi + 1, Yi, n - 1) + (Xi, Yi - 1, n - 1) + (Xi, Yi + 1, n - 1); 即从一个状态可以到其他三种状态,而该状态的值就是其他三种状态的值的和。
AC代码
#include<iostream>
#include<cstdio>
using namespace std;
int num = 0; //方案数
//确定map的大小 因为最多一个方向连走n步(n<=20) 所以上下25,左右50(从中间开始)
int map[25][50]={0};
int N; //要走多少步
int dfs(int i,int j,int n)
{
//边界条件 当要走的是0步时,那就是不走这一种方案
if(n == 0)
return 1;
map[i][j] = 1; //表示现在要访问这一个点
/*接下来是状态的转移
之前在递归函数开头判断该点是否可走 现在是可走再递归*/
int x = 0;
if(map[i+1][j] == 0) x += dfs(i + 1, j, n - 1);
if(map[i][j+1] == 0) x += dfs(i, j + 1, n - 1);
if(map[i][j-1] == 0) x += dfs(i, j - 1, n - 1);
/*把map[i][j]重新置为0,因为表示不要这一步不要访问这个点了
要退回去 让这一步变成访问其他的点*/
map[i][j] = 0;
return x;
}
int main()
{
scanf("%d",&N);
//表示从第0行,第25列走n步的总共走法
printf("%d",dfs(0,25,N));
return 0;
}