深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.
其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次
深度优先算法DFS
模型(以二维直角坐标来举例)
void dfs(int dep) dep表示深度
{
判断边界,如果到了边界返回
尝试每一种可能结果for(i=0;i<n;i++)
{
处理当前步
继续下一步dfs(dep + 1)
}
return ;
}
具体实现伪代码
const int maxn=100;
bool vst[maxn][maxn]; // 访问标记
int map[maxn][maxn]; // 坐标范围
int dir[4][2]={0,1,0,-1,1,0,-1,0}; // 方向向量,(x,y)周围的四个方向
bool CheckEdge(int x,int y) // 边界条件和约束条件的判断
{
if(!vst[x][y] && ...) // 满足条件
return 1;
else // 与约束条件冲突
return 0;
}
void dfs(int x,int y)
{
vst[x][y]=1; // 标记该节点被访问过
if(map[x][y]==G) // 出现目标态G
{
...... // 做相应处理
return;
}
for(int i=0;i<4;i++)
{
if(CheckEdge(x+dir[i][0],y+dir[i][1])) // 按照规则生成下一个节点
dfs(x+dir[i][0],y+dir[i][1]);
}
return; // 没有下层搜索节点,回溯
}
int main()
{
......
return 0;
}*
例题1:方格分割(第八届蓝桥杯省赛题目)
6×6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。
如图:p1.png, p2.png, p3.png 就是可行的分割法。
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。
请提交该整数,不要填写任何多余的内容或说明文字。
思路:
可以用分割格子的线来求,因为线也是关于(3,3)点对称的,
所以可以初始化从点(3,3)开始用深搜同时走对称的两条线。
因为旋转对称属于同一种分法,所以求得的结果除以4即是答案*/
#include<bits/stdc++.h>
using namespace std;
int dir[4][2] = {0,1, 0,-1, 1,0, -1,0}; //定义一个二维数组来存放搜索方向
const int maxn=8;
int vis[maxn][maxn];
int ans = 0; //ans统计次数
void dfs(int x, int y)
{
if(x == 0 || y == 0 || x == 6 || y == 6)
{
ans++;
return ;
}
for(int i = 0; i < 4; i++)
{
int x1 = x + dir[i][0];
int y1 = y + dir[i][1];
int x2 = 6 - x1;
int y2 = 6 - y1;
if(x1 >= 0 && y1 >= 0 && x1 <= 6 && y1 <= 6)
{
if(!vis[x1][y1]) //逻辑取反
{
vis[x1][y1] = vis[x2][y2] = 1;
dfs(x1,y1);
vis[x1][y1] = vis[x2][y2] = 0;
}
}
}
}
int main(){
memset(vis, 0, sizeof(vis)); //vis数组初始化0的快捷方法
vis[3][3] = 1;
dfs(3,3);
printf("%d\n",ans/4);
return 0;
}
例题2:
【问题描述】
小明最近喜欢搭数字积木。一共有10块积木,每个积木上有一个数字,0~9。
搭积木规则:
每个积木放到其它两个积木的上面,并且一定比下面的两个积木数字小。
最后搭成4层的金字塔形,必须用完所有的积木。
0
1 2
3 4 5
6 7 8 9
请你计算这样的搭法一共有多少种?
#include<stdio.h>
#include<stdlib.h>
int visited[10]={0}; //查看该元素是否被访问 全部赋值为0
int a[10]={0,1,2,3,4,5,6,7,8,9};
int sum=0; //定义全局变量sum来进行对符合条件的组合计数
int test(int n){ //判断基本符合条件
if(n==2){ //当三个积木 第二层时
if(a[0]<a[1]&&a[0]<a[2]){
return 1;
}
return 0;
}
else if(n==5){ //六个积木 第三层时
if(a[1]<a[3]&&a[1]<a[4]&&a[2]<a[4]&&a[2]<a[5]){
return 1;
}
return 0;
}
else if(n==9){ //10个积木 第四层时
if(a[3]<a[6]&&a[3]<a[7]&&a[4]<a[7]&&a[4]<a[8]&&a[5]<a[8]&&a[5]<a[9]){
sum++;
return 1;
}
return 0;
}
else
return 1;
}
void dfs(int n){
int i;
for(i=0;i<10;i++){
if(!visited[i]){ //如果不对则visited[i]=0
visited[i]=1; //visited赋值为1 代表已经访问
a[n]=i;
if(!test(n)){ //如果test[n]=0 则表示不符合条件
visited[i]=0;
continue;
}
dfs(n+1);
visited[i]=0;
}
}
}
int main(){
dfs(0);
printf("%d\n",sum);
return 0;
}