【这里不用看】这是一道神奇的题,它之所以神奇不是因为它有多实用,也不是因为它有多难,而是因为它可以用两种方法解出来:一种是深搜(DFS),另一种是广搜(BFS)。所以学长连着两天把这道题叫我们用这两种方法练习,结果第二天还真的有个boy上去就用了第一天的方法把代码bia上去然后显示两分钟就完成了,感觉很厉害的样子呢~(不是我不是我不是我)
【题目】
Oil Deposits
Problem Description
1 1 * 3 5 *@*@* **@** *@*@* 1 8 @@****@* 5 5 ****@ *@@*@ *@**@ @@@*@ @@**@ 0 0
0 1 2 2
【题意】
自从进协会我已经很久没看到中文题了TAT,渣渣英语谁来救救我!
说是第一行输入行列数(输入行数为0时结束),下面紧跟着的几行几列由*和@组成,要求输出一个数字,表示有几组连着的@。
注意这里面相邻斜着的@也算是连着的!
【思路】
先说深搜吧,深搜的原理就是顺着一条路直接找到头再拐回来找其他的路,对于这道题来讲,它拐回来时是不需要再把标记重置的,踩过一遍之后草就死了,所以当我们找到一个@并开始找跟它连在一起的@以后,直接把连在一起的@标记已访问,回去时访问不重置,那么跟这个@连着的这一片@就都不用再检查了,下次再进入函数,就是从另外的没遍历过的@开始找。这样的话,我们从main函数里传递给dfs函数几个@的坐标,就有几个连在一起的@群了。
至于广搜,原理跟深搜是一样的,但是广搜用了15ms,深搜用了46ms,更省了点时间。广搜需要用到队列,而坐标是二维的,这就要求推入队列时要把储存着坐标位置信息的结构推进去,要建立结构体,推入队列的都是将要被检查的元素,当队列非空时取出元素依次检查。
【代码】
DFS:
<pre name="code" class="cpp">#include<cstdio>
#include<string.h>
#define N 101
using namespace std;
int dir[8][2]={{1,0},{-1,0},{0,1},{0,-1},{1,1},{-1,1},{1,-1},{-1,-1}};//由于斜着也算连着,所以设置8个方向
int mark[N][N],b[N][N],m,n,ans;//mark[][]用来检查该位置元素是否被遍历,是为1,否为0;至于b[][]额……只是一个迷の数组;
char a[N][N];
int dfs(int x,int y);
int main()
{
while(scanf("%d%d",&m,&n)&&m)
{
ans=0;
memset(b,0,sizeof(b));
memset(mark,0,sizeof(mark));//输入数据可能有多组,所以每次计算之前都要重置mark每一项都为0,表示都还没有遍历,ans也要重置为0;
for(int i=0;i<m;i++)//输入地图数据
{
for(int j=0;j<n;j++)
{
char aa;
o:scanf("%c",&aa);
if(aa=='\n'||aa=='\0')
goto o;
else if(aa=='@')
b[i][j]=1;
a[i][j]=aa;
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(b[i][j]==1&&mark[i][j]==0)//如果@没有遍历,就让答案ans+1,
{
ans++;
dfs(i,j);//实际上dfs函数在这里的作用就是把跟被检查的@连在一起的@全部标记为已遍历。
}
}
}
printf("%d\n",ans);
}
}
int dfs(int x,int y)
{
mark[x][y]=1;//标记此坐标为已遍历
for(int i=0;i<8;i++)
{
int nx=x+dir[i][0];//nx、ny为以原始x,y值为基础向8个方向走一步时新坐标的数值
int ny=y+dir[i][1];
if(nx>=0&&ny>=0&&nx<m&&ny<n&&!mark[nx][ny]&&a[nx][ny]!='*')//检查新坐标是否越界、是否已被遍历,是不是@符号
{
dfs(nx,ny);//符合条件的话标记此坐标
}
}
}
BFS:
<pre name="code" class="cpp">#include<cstdio>
#include<string.h>
#include<queue>//BFS需要队列来辅助
using namespace std;
int m,n,ans,vis[101][101],dir[8][2]={1,0,-1,0,0,1,0,-1,1,-1,1,1,-1,-1,-1,1};
char a[101][101];
struct inf{ int x,y; }xy;//结构,用来做压缩包把坐标合二为一
queue <inf> q;
void bfs(void);
int main()
{
while(~scanf("%d%d",&m,&n)&&m)
{
while(!q.empty())
q.pop();//重置队列
memset(vis,0,sizeof(vis));//重置访问标记
ans=0;
getchar();
for(int i=0;i<m;i++)//输入数据
{
scanf("%s",&a[i]);
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(a[i][j]=='@'&&vis[i][j]==0)//此处原理同DFS
{
xy.x=i;xy.y=j;//压缩坐标
q.push(xy);//入队
bfs();//进入bfs进行标记
ans++;
}
}
}
printf("%d\n",ans);
}
}
void bfs(void)
{
while(!q.empty())
{
xy=q.front();//出队,开始标记此元素并检测其周围元素是否有@符号
q.pop();//这个……别忘了就好
int X=xy.x;
int Y=xy.y;
vis[X][Y]=1;//标记
for(int i=0;i<8;i++)
{
int nx=X+dir[i][0];
int ny=Y+dir[i][1];
if(a[nx][ny]=='@'&&nx>=0&&nx<m&&ny>=0&&ny<n&&vis[nx][ny]==0)
{
vis[nx][ny]=1;
xy.x=nx;xy.y=ny;
q.push(xy);//符合条件的将此节点作为起点先推入队,放到后面来检查此节点周围是不是还存在相邻@符
}
}
}
}
唯一两种方法都一次过掉的题,虽然不难但是总要写个博客纪念一下~~~!