描述
给你一张某一海域卫星照片,你需要统计:
1. 照片中海岛的数目
2. 照片中面积不同的海岛数目
3. 照片中形状不同的海岛数目
其中海域的照片如下,"."表示海洋,"#"表示陆地。在"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。
.####..
.....#.
####.#.
.....#.
..##.#.
上图所示的照片中一共有4座岛屿;其中3座面积为4,一座面积为2,所以不同面积的岛屿数目是2;有两座形状都是"####",所以形状不同的岛屿数目为3。
输入
第一行包含两个整数:N 和 M,(1 ≤ N, M ≤ 50),表示照片的行数和列数。
以下一个 N * M 的矩阵,表示表示海域的照片。
输出
输出3个整数,依次是照片中海岛的数目、面积不同的海岛数目和形状不同的海岛数目。
样例输入
5 7
.####..
.....#.
####.#.
.....#.
..##.#.
样例输出
4 2 3
解题思路
第一个结果和第二个结果很好处理,关键在于第三个如何标记岛屿的形状。由于范围比较小,开始便想着每一个岛屿都存在一个50*50的数组中,每次遍历所有的岛屿数组判断是否出现过,为了便于比较,在存放时每个点的位置减去起始点的位置。这样过了40分……
后来发现
5 4
..#.
.###
....
..#.
..##
像这样的一组数据是不能得出正确结果的,第一个岛屿起始点位置是(0,2),第二行第一列的位置是(1,1),相减得(1,-1),很明显不对。
最终改成一维数组来存放数组的形状,事先将地图从左上到右下依次编号,在数组中存放编号即可(为了便于比较,每个位置应减去起始点的编号值)。
代码实现
#include <iostream>
#include <cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define maxn 6
bool varea[maxn]; //标记面积值是否出现过
char maps[maxn][maxn],str[maxn];
int a[maxn][maxn*maxn],visit[maxn][maxn];
int m,n,area,countt;
int dir[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
queue<pair<int,int> >qu;
void bfs(int x,int y)
{
int cha=x*n+y;
qu.push(make_pair(x,y));
while(!qu.empty())
{
int xx=qu.front().first;
int yy=qu.front().second;
a[countt][area]=(xx*n+yy)-cha; //存放岛屿形状
area++;
qu.pop();
for(int i=0; i<4; i++)
{
int nx=xx+dir[i][0];
int ny=yy+dir[i][1];
if(nx>=0&&nx<m&&ny>=0&&ny<n&&maps[nx][ny]=='#'&&!visit[nx][ny])
{
qu.push(make_pair(nx,ny));
visit[nx][ny]=1;
}
}
}
}
int main()
{
int ans,ansarea,ansdif; //分别代表三个结果值
bool flag;
while(~scanf("%d %d%*c",&m,&n))
{
ans=0;
ansarea=0;
ansdif=0;
countt=-1;
memset(varea,0,sizeof(varea));
memset(a,0,sizeof(a));
memset(visit,0,sizeof(visit));
for(int i=0; i<m; i++)
{
scanf("%s",str);
for(int j=0; j<n; j++)
maps[i][j]=str[j];
}
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
{
if(!visit[i][j]&&maps[i][j]=='#')
{
visit[i][j]=1;
countt++;
if(countt==0) ansdif++;
area=0;
bfs(i,j);
if(!varea[area])
{
ansarea++;
varea[area]=1;
}
flag=true;
for(int k=0; k<countt; k++)
{
flag=true;
for(int p=0; p<maxn*maxn-1; p++)
{
if(a[countt][p]!=a[k][p])
{
flag=false;
break;
}
}
if(flag) break;
}
if(!flag) ansdif++;
ans++;
}
}
}
printf("%d %d %d\n",ans,ansarea,ansdif);
}
return 0;
}
PS:
由于内存分配的原理,数组(1,-1)的地址会是上一行的最末尾位置的地址,那么原来的方法的也可以通过啦,贴在这里警示自己以后不可以再考虑不到这样易错的情况。
#include <iostream>
#include <cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define maxn 54
bool varea[maxn*maxn];
char maps[maxn][maxn],str[maxn];
int a[1500][100][100],visit[maxn][maxn];
int m,n,area,countt;
int dir[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
queue<pair<int,int> >qu;
void bfs(int x,int y)
{
qu.push(make_pair(x,y));
while(!qu.empty())
{
int xx=qu.front().first;
int yy=qu.front().second;
a[countt][xx-x][yy-y]=1;
qu.pop();
for(int i=0; i<4; i++)
{
int nx=xx+dir[i][0];
int ny=yy+dir[i][1];
if(nx>=0&&nx<m&&ny>=0&&ny<n&&maps[nx][ny]=='#'&&!visit[nx][ny])
{
area++;
qu.push(make_pair(nx,ny));
visit[nx][ny]=1;
}
}
}
}
int main()
{
int ans,ansarea,ansdif;
bool flag;
while(~scanf("%d %d%*c",&m,&n))
{
ans=0;
ansarea=0;
ansdif=0;
countt=-1;
memset(varea,0,sizeof(varea));
memset(a,0,sizeof(a));
memset(visit,0,sizeof(visit));
for(int i=0; i<m; i++)
{
scanf("%s",str);
for(int j=0; j<n; j++)
{
maps[i][j]=str[j];
}
}
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
{
if(!visit[i][j]&&maps[i][j]=='#')
{
countt++;
if(countt==0) ansdif++;
area=0;
bfs(i,j);
if(!varea[area])
{
ansarea++;
varea[area]=1;
}
flag=true;
for(int k=0; k<countt; k++)
{
flag=true;
for(int p=0; p<100; p++)
{
for(int q=0; q<100; q++)
{
if(a[countt][p][q]!=a[k][p][q])
{
flag=false;
break;
}
}
if(!flag)break;
}
if(flag) break;
}
if(!flag)
ansdif++;
ans++;
}
}
}
printf("%d %d %d\n",ans,ansarea,ansdif);
}
return 0;
}