题目地址
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=838&page=show_problem&problem=513
题目分析
就是搜索一个二维数组里面的'@'联通集,并求联通集的个数,做法是:用二维数组存储输入的字符,然后找dfs()的第一个起点,因为不是有向图所以可以从二维数组的第一个'@'开始进行dfs()搜索。dfs()的写法就是递归,因为要求递归找到对应起点的所有子孙并标上序号,所以还需要一个idx[][]
去存储每个'@'的所属连通集序号。dfs()写法中递归的条件也是需要注意的,具体的要求是:
- 点在二维数组范围内
- 点没有被标号为某个联通集序号
- 点为'@'
代码:
#include
using namespace std;
const int maxn = 210;
int m,n;
char buf[maxn][maxn];
int idx[maxn][maxn];
bool inside(int r,int c){
if(r < 0 || r >= m || c < 0 || c >= n) return false;
return true;
}
void dfs(int r,int c ,int id){
idx[r][c] = id;
for(int dr = -1;dr <= 1;dr++){
for(int dc = -1;dc <= 1;dc++){
if(dr != 0 || dc != 0){ //扫描的结点不是本身
if(inside(r+dr,c+dc) && idx[r+dr][c+dc] == 0 && buf[r+dr][c+dc] == '@') //周边的结点满足条件则递归:1:不越界,2:没有被标号;3:为'@'
dfs(r+dr,c+dc,id);
}
}
}
}
int main(void){
while(scanf("%d%d",&m,&n) == 2 && m && n){
for(int i = 0;i < m;i++){
scanf("%s",buf[i]);
}
memset(idx,0,sizeof(idx));
int cnt = 0;
for(int i = 0;i < m;i++){
for(int j = 0;j < n;j++){
if(buf[i][j] == '@' && idx[i][j] == 0)
dfs(i,j,++cnt); //深度优先搜索
}
}
printf("%d\n",cnt);
}
return 0;
}
补充一种和刘汝佳不同的做法,不需要存储联通块的序号,更加简单:
#include
using namespace std;
const int maxn = 210;
int m,n;
char buf[maxn][maxn];
bool inside(int r,int c){
if(r < 0 || r >= m || c < 0 || c >= n) return false;
return true;
}
void dfs(int r,int c){
buf[r][c] = '*'; //将遍历过的所有连通块全部变为*;就可以不用存储连通块序号的数组了
//循环遍历移动8个方向
for(int dr = -1;dr <= 1;dr++){
for(int dc = -1;dc <= 1;dc++){
//判断(r+dr,c+dc)是不是在区域内,并且为油田
if(inside(r+dr,c+dc) && buf[r+dr][c+dc] == '@')
dfs(r+dr,c+dc);
}
}
}
int main(void){
while(scanf("%d%d",&m,&n) == 2 && m && n){
for(int i = 0;i < m;i++){
scanf("%s",buf[i]);
}
int cnt = 0;
for(int i = 0;i < m;i++){
for(int j = 0;j < n;j++){
if(buf[i][j] == '@')
{
dfs(i,j);
cnt++;
}
}
}
printf("%d\n",cnt);
}
return 0;
}