这里附上题目链接:拯救oibh总部。
这是一道十分显然的搜索题!!!
思路解析
题意转换
洪水应该是从地图边界向内渗透,但围墙 * 会将洪水挡住。也就是说洪水只能沿着没有围墙 * 的路径向内渗透。
我们可以得到如下结论:
若一块重要区域0能被洪水渗透,说明该重要区域0与地图边界之间至少有1条路径。
于是有2种搜索方向:
-
从外向内搜:
从地图的4条边界上的重要区域0出发,向内搜索,将搜索路径上的重要区域0全部用围墙 * 覆盖(因为这些0是可以通到边界的)。若遇到围墙 * ,则说明此条路径不通。最后遍历整个地图,计算出剩余的重要区域0; -
从内向外搜:
从地图上的每个未被标记的重要区域0出发向外搜索,将搜索路径上的所有重要区域0标记为已访问:
①若能到达边界,则说明本次搜索中访问的所有重要区域0都会被洪水淹没,将本次搜索记录下所标记的重要区域0的总数sum清零;
②若不能到达边界,则记录下所标记的重要区域0的总数sum,最后将这些总数相加。(想想为什么不能将搜索路径上的重要区域全部用围墙 * 覆盖???)
1.DFS——深度优先搜索
(1) 从外向内搜:
AC代码:
#include <stdio.h>
#include <stdlib.h>
int n,m;//给出的地图有n行m列
char map[500][505];
int location[4][2]={{-1,0},{1,0},{0,1},{0,-1}};//4个方位
int check(int round,int colume)//检查
{
if(round>=0&&round<n&&colume>=0&&colume<m)//若坐标未越界
{
return 1;
}
return 0;//返回假值
}
int DFS(int round,int colume)
{
int i,x,y;
if(!check(round,colume))//若坐标越界
{
return 0;
}
if(map[round][colume]=='*')//若遇到围墙 *
{
return 0;//说明此条路径不通
}
map[round][colume]='*';//将搜索路径上的重要区域全部用围墙 * 覆盖
for(i=0;i<4;i++)//沿四个方向继续搜索
{
//计算新坐标
x=round+location[i][0];
y=colume+location[i][1];
DFS(x,y);
}
return 0;
}
int main()
{
int i,j,sum=0;
//输入数据
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%s",map[i]);
}
//沿外边界搜索
if(n>1)
{
for(j=0;j<m;j++)
{
//n>1说明地图至少有2行
DFS(0,j);//上边界
DFS(n-1,j);//下边界
}
}
else
{
for(j=0;j<m;j++)
{
DFS(0,j);
}
}
for(i=1;i<n-1;i++)//从左右边界出发
{
DFS(i,0);
DFS(i,m-1);
}
//遍历整个地图,计算剩余的重要区域0
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(map[i][j]=='0')
{
sum++;
}
}
}
//输出结果
printf("%d",sum);
return 0;
}
(2)从内向外搜
AC代码:
#include <stdio.h>
#include <stdlib.h>
char map[500][505];//地图
int visit[500][505];//标记数组
int n,m,sign,sum;//给出的地图有n行m列
int location[4][2]={{-1,0},{1,0},{0,1},{0,-1}};//4个方位
int check(int round,int colume)//检查
{
if(round>0&&round<n-1&&colume>0&&colume<m-1)//若坐标未到达边界
{
return 1;
}
return 0;//返回假值
}
int DFS(int round,int colume)
{
int i,x,y;
for(i=0;i<4;i++)//沿四个方向继续搜索
{
x=round+location[i][0];
y=colume+location[i][1];
if((!check(x,y))&&map[x][y]=='0')//若能到达边界上的重要区域0
{
sign=1;
continue;
}
if(map[x][y]=='*'||!check(x,y)||visit[x][y])
{
continue;
}
visit[x][y]=1;//将未访问的此格标记为已访问
sum++;
DFS(x,y);
}
return 0;
}
int main()
{
int i,j,cnt=0;
//输入数据
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%s",map[i]);
}
//
for(i=1;i<n-1;i++)
{
for(j=1;j<m-1;j++)
{
if(map[i][j]=='0'&&!visit[i][j])
{
visit[i][j]=1;//标记为已访问
sum=1;
sign=0;
DFS(i,j);//开始搜索
if(sign)//若能到达边界
{
sum=0;
}
cnt+=sum;
}
}
}
//输出结果
printf("%d",cnt);
return 0;
}
~~分割线~~
实际上,采用这种搜索方向也可以不使用标记数组。这时,我们只需要(逐行逐列地)从每一个重要区域出发进行试探。若发现不能到达边界,则说明幸存的重要区域加一。
//逐行逐列找重要区域
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(map[i][j]=='0')
{
DFS(i,j);
if(!sign)//若不能到达边界
{
sum++;//幸存的重要区域加一
}
sign=0;//将标志复原
}
}
}
完整代码
#include <stdio.h>
#include <stdlib.h>
int n,m,sign;//给出的地图有n行m列
char map[500][505];
int location[4][2]={{-1,0},{1,0},{0,1},{0,-1}};//4个方位
int check(int round,int colume)//检查
{
if(round>0&&round<n-1&&colume>0&&colume<m-1)//若坐标未到达边界
{
return 1;
}
return 0;//返回假值
}
int DFS(int round,int colume)
{
int i,x,y;
if(!check(round,colume))//若能到达边界
{
sign=1;
return 0;
}
if(map[round][colume]=='*')
{
return 0;
}
for(i=0;i<4;i++)//沿四个方向继续搜索
{
x=round+location[i][0];
y=colume+location[i][1];
DFS(x,y);
}
return 0;
}
int main()
{
int i,j,sum=0;
//输入数据
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%s",map[i]);
}
//逐行逐列找重要区域
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(map[i][j]=='0')
{
DFS(i,j);
if(!sign)//若不能到达边界
{
sum++;//幸存的重要区域加一
}
sign=0;//将标志复原
}
}
}
//输出结果
printf("%d",sum);
return 0;
}
只不过,因为没有剪枝,这样内存会过大!!!(答案是正确的)
2.BFS——广度优先搜索
(1).从外向内搜:
AC代码:
#include <stdio.h>
#include <stdlib.h>
int n,m;//给出的地图有n行m列
char map[500][505];//地图
int location[4][2]={{-1,0},{1,0},{0,1},{0,-1}};//4个方位
struct box//box意为方格
{
int x;
int y;
}queue[25005];
int check(int round,int colume)//检查
{
if(round>=0&&round<n&&colume>=0&&colume<m)//若坐标未越界且
{
return 1;
}
return 0;//返回假值
}
int BFS(int round,int colume)
{
int i,x,y;
int head=0,tail=0;//构建队首、队尾
//第一个结点从队尾入队
queue[0].x=round;
queue[0].y=colume;
map[round][colume]='*';
tail++;
while(head<tail)
{
for(i=0;i<4;i++)
{
//计算新结点坐标
x=queue[head].x+location[i][0];
y=queue[head].y+location[i][1];
if(!check(x,y))//若坐标不合格
{
continue;
}
if(map[x][y]=='*')//围墙不入队
{
continue;
}
//子结点入队
queue[tail].x=x;
queue[tail].y=y;
//
map[x][y]='*';
tail++;
}
head++;//所有子结点都入队的父结点出队
}
return 0;
}
int main()
{
int i,j,sum=0;
//输入数据
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%s",map[i]);
}
//沿外边界向内搜索
if(n>1)
{
for(j=0;j<m;j++)
{
if(map[0][j]!='*')
{
BFS(0,j);
}
if(map[n-1][j]!='*')
{
BFS(n-1,j);
}
}
}
else
{
for(j=0;j<m;j++)
{
if(map[0][j]!='*')
{
BFS(0,j);
}
}
}
for(i=1;i<n-1;i++)
{
if(map[i][0]!='*')//不能从围墙出发开始搜索
{
BFS(i,0);
}
if(map[i][m-1]!='*')//不能从围墙出发开始搜索
{
BFS(i,m-1);
}
}
//遍历整个地图,计算结果
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(map[i][j]=='0')//若有幸存的重要区域
{
sum++;
}
}
}
//输出结果
printf("%d",sum);
return 0;
}
(2).从内向外搜:
AC代码:
#include <stdio.h>
#include <stdlib.h>
char map[500][505];
int visit[500][505];
int n,m,sign,sum;//给出的地图有n行m列
int location[4][2]={{-1,0},{1,0},{0,1},{0,-1}};//4个方位
struct box
{
int x,y;
}queue[250030];
int check(int round,int colume)//检查
{
if(round>0&&round<n-1&&colume>0&&colume<m-1)//若坐标未到达边界
{
return 1;
}
return 0;//返回假值
}
int BFS(int round,int colume)
{
int i,x,y;
int head=0,tail=0;//构建队首、队尾
//第一个结点从队尾入队
queue[tail].x=round;
queue[tail].y=colume;
tail++;
while(head<tail)
{
for(i=0;i<4;i++)
{
x=queue[head].x+location[i][0];
y=queue[head].y+location[i][1];
if((!check(x,y))&&map[x][y]=='0')//若能到达边界上的重要区域0
{//边界上的重要区域不能入队
sign=1;//转换标记
continue;
}
if(map[x][y]=='*'||visit[x][y])//这里不用再对(x,y)做坐标检查
{
continue;
}
//重要区域0入队
queue[tail].x=x;
queue[tail].y=y;
tail++;
visit[x][y]=1;//将未访问的此格标记为已访问
sum++;
}
head++;//所有子结点都入队的头结点出队
}
return 0;
}
int main()
{
int i,j,cnt=0;
//输入数据
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
{
scanf("%s",map[i]);
}
//因为是从内向外搜索,所以从内层开始搜索
for(i=1;i<n-1;i++)
{
for(j=1;j<m-1;j++)
{
if(map[i][j]=='0'&&!visit[i][j])
{
sum=1;
sign=0;
visit[i][j]=1;//标记为已访问
BFS(i,j);//开始搜索
if(sign)//若能到达边界
{
sum=0;
}
cnt+=sum;//累加
}
}
}
//输出结果
printf("%d",cnt);
return 0;
}