图的搜索算法(待填坑)
常见的图搜索算法有DFS(深度优先搜索)和BFS(广度优先搜索),它们常常由递归实现,当然也存在非递归实现。
【例1】海岛问题
问题描述
输入一个长方形表示海域G,判定G中是否只有一个海岛。区域中每个方格位置用0或1表示,其中1表示是高于海平面的小区域,0表示低于海平面的小区域。水平或垂直直接相邻的两个方格称为区域相邻,其他位置的方格不是直接相邻的,高出海平面的连通区域构成海岛。
现在要求你编写一个程序,判断该长方形区域中是否只有一个海岛。如下图所示是一个6×8的海域,有一个海岛。
输入
有多个海域图数据。
每个海域图描述的第1行是两个整数m和n,(0<m,n<100),分别表示海域的行数和列数。接着有m行,每行有n个数字,每个数字是0或1,之间用一个空格分开。
输出
对每个海域图,如果是里面只有一个海岛,那么输出“Yes”,否则输出“No”。
输入样例
6 8
1 1 0 0 1 1 1 1
0 1 1 0 1 0 1 0
1 1 1 1 1 1 1 0
0 1 1 0 0 1 0 0
0 0 1 0 0 1 0 0
0 0 0 0 0 1 1 0
输出样例
Yes
问题分析
本题实质上是一个图的搜索问题,首先通过遍历图中的每个点,对访问过的点进行标记。对于未被访问过且满足要求的点,将其作为起点,向上、下、左、右方向进行深度优先搜索,判断是否满足要求。当被访问的点超出边界或不满足要求或已被遍历,则不进行深度优先搜索操作。
代码实现
#include <iostream>
using namespace std;
const int N = 100;
int mp[N][N];
int dx[4] = {0, 0, -1, 1};
int dy[4] = {1, -1, 0, 0};
int n,m;
bool vis[N][N];
void dfs(int r, int c){
vis[r][c] = true;
for(int i=0; i<4; i++){
int nr = r+dx[i];
int nc = c+dy[i];
if(nr>=0 && nr<m && nc>=0 && nc<n && !vis[nr][nc] && mp[nr][nc]==1){
dfs(nr,nc);
}
}
}
int main(){
while(cin >> m >> n){
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
cin >> mp[i][j];
vis[i][j] = false;
}
}
int res = 0;
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
if(mp[i][j]==1 && !vis[i][j]){
res++;
dfs(i,j);
}
}
}
if(res == 1)
cout<< "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
【例2】石油探测问题
描述
对于一个矩形区域而言,创造大小相等的格子将其划分,使用探测器探测得该格子中是否含有石油。若含有石油,则称该格子为pocket。一个oil deposit含有一个或多个pocket,如果两个pocket相邻,则称它们是同一个oil deposit。求给定区域内oil deposit的个数。
输入
包含多组输入数据。每组输入数据占据一行,每行包括矩形区域行(m)和列(n)被划分的格子数,中间以空格相隔。如果m=0,则结束输入,否则,后面跟上m行数据,每行含有n个字符。每个字符表示一个格子,若包含石油,则标记为‘*’,若不包含石油,则标记为‘@’。
输出
对于每组数据,输入其中包含的oil deposit数,左右、上下、斜方向上相邻的pocket都认为同属一个oil deposit。
样例输入
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
0 0
样例输出1
0
1
2
算法分析
此题与例2相似。
代码实现
#include <iostream>
using namespace std;
const int N = 100;
char mp[N][N];
// x和y的方向向量
int dx[8] = {0, 0, -1, 1, -1, -1, 1, 1};
int dy[8] = {1, -1, 0, 0, -1, 1, -1, 1};
int n,m;
bool vis[N][N];
void dfs(int r, int c){
vis[r][c] = true;
for(int i=0; i<8; i++){
int nr = r+dx[i];
int nc = c+dy[i];
//判断是否出界,是否访问过,是否为目标值
if(nr>=0 && nr<m && nc>=0 && nc<n && !vis[nr][nc] && mp[nr][nc]=='@'){
//cout << "[" << nr << " " << nr << "]" << endl;
dfs(nr,nc);
}
}
}
int main(){
while(cin >> m >> n){
if(m==0)
exit(0);
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
cin >> mp[i][j];
vis[i][j] = false;
}
}
int pocket = 0;
// 枚举每个点,判断是否访问过
for(int i=0; i<m; i++){
for(int j=0; j<n; j++){
//以未访问过的目标值点为起点,标记与该点联通的其他点
if(mp[i][j]=='@' && !vis[i][j]){
pocket++;
dfs(i,j);
}
}
}
cout << pocket << endl;
}
return 0;
}