hihoCoder 156周 岛屿

描述

给你一张某一海域卫星照片,你需要统计:

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值