DFS、BFS和Backtracking模板
区别与联系
区别
DFS多用于连通性问题因为其运行思想与人脑的思维很相似,故解决连通性问题更自然,采用递归,编写简便(但我个人不这样觉得。。。)
DFS的常数时间开销会较少。所以对于一些能用DFS就能轻松解决的,为何要用BFS?
一般来说,能用DFS解决的问题,都能用BFS。
BFS多用于解决最短路问题,其运行过程中需要储存每一层的信息,所以其运行时需要储存的信息量较大,如果人脑也可储存大量信息的话,理论上人脑也可运行BFS。
Backtracking相当于在DFS的基础上进行剪枝。
联系
BFS(显式用队列)
DFS(隐式用栈)(即递归)
当然,对于DFS,用递归可能会造成栈溢出,所以也可以更改为显示栈。
模板
在解答树里进行考虑
DFS/Backtracking
void dfs(int 当前状态)
{
if(当前状态为边界状态)
{
记录或输出
return;
}
for(i=;i<n;i++) //横向遍历解答树所有子节点
{
//扩展出一个子状态。
修改了全局变量
if(子状态满足约束条件)
{
dfs(子状态)
}
恢复全局变量//回溯部分
}
}
BFS
void bfs()
{
q.push(s); //将(起始)首节点加入队列
visited[s]=true; //标记首节点已经被访问
while(!q.empty())
{
int x=q.front();
q.pop();
遍历 x 的各个Next状态 next
{
if(next is legal)
q.push(next); //入队,同时计数或维护等;
}
}
}
DFS(非递归,显式用栈)
//求(sx,sy)到(ex,ey)的其中一条路径
//如果无法到达,返回false
bool dfs()
{
stack<P>s;
vis[sx][sy] = true; //访问
s.push(P(sx, sy)); //入栈
while (!s.empty()) //栈不为空。继续搜索;为空了还没有得到路径,说明无解
{
P p = s.top();
int x = p.first, y = p.second;
if (x == ex && y == ey)
{
while (!s.empty())
{
//打印结果,或进行其它操作
P p = s.top(); s.pop();
printf("%d %d\n", p.first, p.second);
}
return true;
}
int flag = false; //记录是否进入“死胡同”
for(遍历相邻的状态)
{
if(满足条件)
{
vis[nx][ny] = true;
s.push(P(nx, ny));
flag = true;
break; //DFS,选择其中一条路走
}
}
if (!flag)
s.pop(); //周四是墙或已走过,回溯,也就是不断出栈,知道新的栈顶元素有其他出路
}
return false;
}
经典例题
部分和问题(DFS+剪枝)
给定整数a1、a2、...an,判断是否可以从中选出若干个数字,使它们的和恰好为k。(1≤n≤20)
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std; const int maxn = + ;
int n, a[maxn], k;
int vis[maxn]; //已经从前cur项得到和sum
bool dfs(int cur, int sum)
{
if (sum > k) return false; //剪枝
if (cur == n) return sum == k; //如果前n项都计算过了,则返回sum是否等于k //不加上a[i]的情况
if (dfs(cur + , sum)) return true;
//加上a[i]的情况
if (dfs(cur + , sum + a[cur])) return true; //无论是否加上a[i]都不能凑成k,则返回false
return false;
} int main()
{
while (scanf("%d%d", &n, &k) == && n)
{
for (int i = ; i < n; i++)
scanf("%d", &a[i]);
if (dfs(, )) printf("YES\n");
else printf("NO\n");
}
return ;
}
若要求输出所有的方案
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std; const int maxn = + ;
int n, a[maxn],k;
int vis[maxn],flag; bool dfs(int cur, int sum)
{
if (cur == n)
{
if (sum == k)
{
flag = ;
for (int i = ; i < n; i++)
if (vis[i]) printf("%d ", a[i]);
printf("\n");
}
return sum == k;
}
vis[cur] = false; //不加上a[cur]
dfs(cur + , sum); //这里不要return,都是在cur == n时(即遍历完了)在返回true/false vis[cur] = true; //加上a[cur]
dfs(cur + , sum + a[cur]);
} int main()
{
while (scanf("%d%d",&n,&k) == && n)
{
for (int i = ; i < n; i++)
scanf("%d", &a[i]);
flag = ;
dfs(, );
if (flag)
printf("Yes\n");
else printf("No\n");
}
}
联通块问题(DFS)
有一个大小为NxM的园子,雨后积起了水。八联通的积水被认为是连接在一起的。请求出园子里总共有多少个水洼?(N,M≤100)
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std; const int maxn = + ;
const int maxm = + ;
const int dx[] = { -,,,,,,-,- };
const int dy[] = { ,,,,-,-,-, };
int N, M;
char field[maxn][maxm]; //现在位置(x,y)
void dfs(int x, int y)
{
field[x][y] = '.'; //表示已访问 for (int i = ; i < ; i++)
{
int nx = x + dx[i], ny = y + dy[i];
if (nx >= && nx < N && ny >= && ny < M && field[nx][ny] == 'W') dfs(nx, ny);
}
return;
} void slove()
{
int res = ; //记录联通块个数
for(int i = ;i < N;i++)
for (int j = ; j < M; j++)
{
if (field[i][j] == 'W')
{
dfs(i, j); //从有W的地方开始dfs,
res++;
}
}
printf("%d\n", res);
} int main()
{
while (scanf("%d%d",&N,&M) == && N)
{
for (int i = ; i < N; i++)
scanf("%s", field[i]);
slove();
}
return ;
}
最短路问题 (BFS)
给定一个大小为N x M的迷宫。迷宫由通道和墙壁组成,每步可以向邻接的上下左右四格的通道移动。请求出从起点到终点所需的最小步数。(N,M ≤ 100)
#include<stdio.h>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std; typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int maxn = + ;
const int maxm = + ;
const int dx[] = { -,,, };
const int dy[] = { ,,,- };
int N, M;
int sx, sy; //起点
int ex, ey; //终点
char maze[maxn][maxm];
int d[maxn][maxm]; //起点到各个位置的最短距离的数组 //求(sx,sy)到(ex,ey)的最短距离
//如果无法到达,返回INF
int bfs()
{
queue<P>que;
for (int i = ; i < N; i++) //把所有位置初始化为INF
for (int j = ; j < M; j++)
d[i][j] = INF;
que.push(P(sx, sy)); //将起点加入队列,且将距离置为0
d[sx][sy] = ; while (!que.empty())
{
P p = que.front(); que.pop();
if (p.first == ex && p.second == ey) break; //如果到达终点,退出循环,由于bfs的特点,这时得到的就是最短距离 for (int i = ; i < ; i++)
{
int nx = p.first + dx[i], ny = p.second + dy[i];
if(nx >= && nx < N && ny >= && ny < M && maze[nx][ny] != '#' && d[nx][ny] == INF)
{
que.push(P(nx, ny));
d[nx][ny] = d[p.first][p.second] + ;
}
}
}
return d[ex][ey];
}
int main()
{
while (scanf("%d%d",&N,&M) == && N)
{
for (int i = ; i < N; i++)
scanf("%s", maze[i]);
for(int i = ;i < N;i++)
for (int j = ; j < M; j++)
{
if (maze[i][j] == 'S')
{
sx = i; sy = j;
}
if (maze[i][j] == 'G')
{
ex = i; ey = j;
}
}
int dis = bfs();
if (dis == INF) printf("no way\n");
else printf("%d\n", dis);
}
return ;
}
八皇后问题(Backtracking)
在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;
const int N = ;
int map[N][N];
int cnt = ; //记录方案数 /************************打印结果********************/
void Display()
{
printf("--------------解决方案 %d :-------------\n",cnt);
for (int i = ; i < N; i++)
{
for (int j = ; j < N; j++)
{
if (map[i][j] == )
cout << '.';
else
cout << '#';
}
printf("\n");
}
} /*********************判断是否与前面冲突****************/
int Check(int row, int col)
{
int flag = ;
if (row == )
return true;
for (int i = ; i < row; i++)
{
for (int j = ; j < N; j++)
{
if (map[i][j] == )
if (j == col || (fabs(row-i) == fabs(col - j)))
flag = ;
}
}
return flag;
} /**************************按行深搜***********************/
void Dfs(int row)
{
if (row == N)
{
cnt++;
Display();
return;
}
for (int col = ; col < N; col++)
{
if (Check(row, col))
{
map[row][col] = ; //标记
Dfs(row + );
map[row][col] = ; //回溯,修改了的全局变量必须及时还原
}
}
return;
}
int main()
{
Dfs();
return ;
}
迷宫历经问题(DFS非递归)给定一个大小为N x M的迷宫。迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格的通道移动。请求出起点到终点的任意一条路径,若无法到达,输出"no way"
#include<stdio.h>
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std; typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int maxn = + ;
const int maxm = + ;
const int dx[] = { -,,, };
const int dy[] = { ,,,- };
int N, M;
int sx, sy; //起点
int ex, ey; //终点
char maze[maxn][maxm];
bool vis[maxn][maxn]; //求(sx,sy)到(ex,ey)的其中一条路径
//如果无法到达,返回false
bool dfs()
{
stack<P>s;
vis[sx][sy] = true;
s.push(P(sx, sy)); while (!s.empty())
{
P p = s.top();
int x = p.first, y = p.second;
if (x == ex && y == ey)
{
while (!s.empty())
{
P p = s.top(); s.pop();
printf("%d %d\n", p.first, p.second);
}
return true;
}
int flag = false;
for (int i = ; i < ; i++)
{
int nx = x + dx[i], ny = y + dy[i];
if (nx >= && nx < N && ny >= && ny < M && maze[nx][ny] != '#' && vis[nx][ny] == false)
{
vis[nx][ny] = true;
s.push(P(nx, ny));
flag = true;
break; //DFS,选择其中一条路走
}
}
if (!flag)
s.pop(); //周四是墙或已走过,回溯,也就是不断出栈,知道新的栈顶元素有其他出路
}
return false;
}
int main()
{
while (scanf("%d%d", &N, &M) == && N)
{
for (int i = ; i < N; i++)
scanf("%s", maze[i]);
for (int i = ; i < N; i++)
for (int j = ; j < M; j++)
{
if (maze[i][j] == 'S')
{
sx = i; sy = j;
}
if (maze[i][j] == 'G')
{
ex = i; ey = j;
}
}
if (!dfs()) printf("no way\n");
}
return ;
}
参考链接:
https://www.zhihu.com/question/28549888
https://blog.csdn.net/fightforyourdream/article/details/12866861
https://blog.csdn.net/renwotao2009/article/details/52993277
https://blog.csdn.net/lalor/article/details/6845788
https://blog.csdn.net/HUSTLX/article/details/52163923
DFS、BFS和Backtracking模板的更多相关文章
- DFS/BFS+思维 HDOJ 5325 Crazy Bobo
题目传送门 /* 题意:给一个树,节点上有权值,问最多能找出多少个点满足在树上是连通的并且按照权值排序后相邻的点 在树上的路径权值都小于这两个点 DFS/BFS+思维:按照权值的大小,从小的到大的连有 ...
- 【DFS/BFS】NYOJ-58-最少步数(迷宫最短路径问题)
[题目链接:NYOJ-58] 经典的搜索问题,想必这题用广搜的会比较多,所以我首先使的也是广搜,但其实深搜同样也是可以的. 不考虑剪枝的话,两种方法实践消耗相同,但是深搜相比广搜内存低一点. 我想,因 ...
- ID(dfs+bfs)-hdu-4127-Flood-it!
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4127 题目意思: 给n*n的方格,每个格子有一种颜色(0~5),每次可以选择一种颜色,使得和左上角相 ...
- [LeetCode] 130. Surrounded Regions_Medium tag: DFS/BFS
Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. A reg ...
- HDU 4771 (DFS+BFS)
Problem Description Harry Potter has some precious. For example, his invisible robe, his wand and hi ...
- DFS/BFS视频讲解
视频链接:https://www.bilibili.com/video/av12019553?share_medium=android&share_source=qq&bbid=XZ7 ...
- POJ 3083 -- Children of the Candy Corn(DFS+BFS)TLE
POJ 3083 -- Children of the Candy Corn(DFS+BFS) 题意: 给定一个迷宫,S是起点,E是终点,#是墙不可走,.可以走 1)先输出左转优先时,从S到E的步数 ...
- [LeetCode]695. 岛屿的最大面积(DFS/BFS)、200. 岛屿数量(DFS/BFS待做/并差集待做)
695. 岛屿的最大面积 题目 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被 ...
- POJ2308连连看dfs+bfs+优化
DFS+BFS+MAP+剪枝 题意: 就是给你一个10*10的连连看状态,然后问你最后能不能全部消没? 思路: 首先要明确这是一个搜索题目,还有就是关键的一点就是连连看这个游戏是 ...
随机推荐
- 【POJ 2407】 Relatives
[题目链接] 点击打开链接 [算法] 欧拉函数 [代码] #include <algorithm> #include <bitset> #include <cctype& ...
- laravel 自定义分页 offset 和 limit 的使用
laravel 本身有一个自带的快速分页方法 paginate,只需要传入每页显示多少条数据就可以 了,但是如果想使用自定义从哪里开始呢,这时候就可以使用offset 和 limit 的组合,offs ...
- java io流中怎么在一个文本中追加字符串
1/ FileOutputStream(File file, boolean append)2/FileWriter(File file, boolean append) 不管哪一种IO都有 appe ...
- centos7安装xtrabackup
1.安装percona依赖库: yum install http://www.percona.com/downloads/percona-release/redhat/0.1-4/percona-re ...
- centos7用lvm扩展xfs文件系统的根分区
centos7中默认使用的是xfs文件系统,此文件系统的特点,可以另外查找资料,这里说一下对文件系统的扩容: 1.先看一下没扩容之前的分区大小 2.添加一块新磁盘,并进行分区.格式化(格式化的时候用m ...
- cclfow_小流程设计
官网演示地址:http://demo.ccflow.org/ 流程的主要需求: 新建流程 登陆系统后台进行设置流程:鼠标右键流程树 选择新建子级类别 输入子级类别名称:流程演示,点击确定即可 点击流程 ...
- dijkstra算法的应用(poj2387)+堆优化【还没学C艹很尴尬,不理解的先不写了,未完,待续...】
一题非常简单的最短路题目,但是我就是很撒比的错在了,1.初始化:2.判断重边 堆优化,使用优先队列的堆优化:复杂度:O(ElogE); #include <stdio.h> #includ ...
- PAT 1035 插入与归并(25)
原题:https://pintia.cn/problem-sets/994805260223102976/problems/994805286714327040传送门: 根据维基百科的定义: 插入排序 ...
- IE css hack整理
CSS hack由于不同的浏览器,比如Internet Explorer 6,Internet Explorer 7,Mozillafirefox等,对CSS的解析认识不一样,因此会导致生成的页面效果 ...
- magento layout xml 小结
基础概念: http://magebase.com/magento-tutorials/demystifying-magentos-layout-xml-part-1/ 调试方案函数: $this-& ...