hiho一下 第156周 岛屿

题意

给你一张某一海域卫星照片,你需要统计:
1. 照片中海岛的数目
2. 照片中面积不同的海岛数目
3. 照片中形状不同的海岛数目
其中海域的照片如下,”.”表示海洋,”#”表示陆地。在”上下左右”四个方向上连在一起的一片陆地组成一座岛屿。

.####..  
.....#.  
####.#.  
.....#.  
..##.#. 

上图所示的照片中一共有4座岛屿;其中3座面积为4,一座面积为2,所以不同面积的岛屿数目是2;有两座形状都是”####”,所以形状不同的岛屿数目为3。

解题思路

这道题需要我们求出三个数值,一是海岛数目,二是面积不同的海岛数目,三是形状不同的海岛数目。
第一个小问题是最基本的搜索问题。一般我们可以从上到下、从左到右扫描,直到发现一个没有处理过的’#’。然后从这个’#’开始沿着4个方向扩展,把连在一起的’#’都找出来。这些连在一起的’#’就组成一个岛屿。
第二个小问题很容易就可以在第一个小问题的基础上求得。我们只需在dfs/bfs找岛屿的过程中保存一下’#’的数目即可。
第三个小问题判断形状相同,我们可以用相对位置来判断。
以样例数据为例,第一行的”####”对应的坐标(行从上到下,列从左到右)依次是(0, 1)(0, 2)(0, 3)(0, 4)。如果我们以其先最上其次最左的点,也就是(0, 1)为基准的话,相对位置序列是(0, 0)(0, 1)(0, 2)(0, 3)。
同理第三行的”####”的坐标依次是(2, 0)(2, 1)(2, 2)(2, 3),其相对位置也是(0, 0)(0, 1)(0, 2)(0, 3)。两个岛屿形状相同,当且仅当它们的相对位置序列完全相同。
所以我们需要在dfs/bfs找岛屿的过程中把陆地的位置都保存下来。

以样例为例,处理过程为:
输入:

5 7
.####..
.....#.
####.#.
.....#.
..##.#.

DFS搜索,用一个二维数组记录岛屿:

0111100
0000020
3333020
0000020
0044020

在搜索时,用一个结构体记录每个岛屿的位置:

1 (0,1)(0,2)(0,3)(0,4)
2 (1,5)(2,5)(3,5)(4,5)
3 (2,0)(2,1)(2,2)(2,3)
4 (4,2)(4,3)

转换相对位置:

1 (0,0)(0,1)(0,2)(0,3)
2 (0,0)(1,0)(2,0)(3,0)
3 (0,0)(0,1)(0,2)(0,3)
4 (0,0)(0,1)

输出结果:

4 2 3

参考代码

#include <bits/stdc++.h>
using namespace std;
#define MAXN 51
char M[MAXN][MAXN];
int visited[MAXN][MAXN];
bool used[MAXN*MAXN];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int n,m;
struct Node{
    int k;
    int x[MAXN*MAXN],y[MAXN*MAXN];
}P[MAXN*MAXN];
bool check(int x,int y){
    if (!visited[x][y] && 0<=x && x<n && 0<=y && y<m && M[x][y]=='#')
        return 1;
    return 0;
}
void DFS(int x,int y,int cnt){
    visited[x][y]=cnt;
    P[cnt].x[P[cnt].k]=x;
    P[cnt].y[P[cnt].k]=y;
    P[cnt].k++;
    for (int i=0;i<4;i++){
        int x2=x+dx[i];
        int y2=y+dy[i];
        if (check(x2,y2))
            DFS(x2,y2,cnt);
    }
}
int main(){
    while (cin>>n>>m){
        for (int i=0;i<n;i++)
            cin>>M[i];
        memset(visited,0,sizeof(visited));
        for (int i=0;i<MAXN*MAXN;i++)
            P[i].k=0;
        int cnt=1;
        for (int i=0;i<n;i++){
            for (int j=0;j<m;j++){
                if (!visited[i][j] && M[i][j]=='#'){
                    DFS(i,j,cnt++);
                }
            }
        }
        /*
        for (int i=0;i<n;i++){
            for (int j=0;j<m;j++)
                cout<<visited[i][j];
            cout<<endl;
        }
        for (int i=1;i<cnt;i++){
            cout<<i<<" ";
            for (int j=0;j<P[i].k;j++)
                cout<<"("<<P[i].x[j]<<","<<P[i].y[j]<<")";
            cout<<endl;
        }
        */
        for (int i=1;i<cnt;i++){
            for (int j=1;j<P[i].k;j++){
                P[i].x[j]-=P[i].x[0];
                P[i].y[j]-=P[i].y[0];
            }
            P[i].x[0]=P[i].y[0]=0;
        }
        /*
        for (int i=1;i<cnt;i++){
            cout<<i<<" ";
            for (int j=0;j<P[i].k;j++)
                cout<<"("<<P[i].x[j]<<","<<P[i].y[j]<<")";
            cout<<endl;
        }
        */
        int cnt2=0;
        memset(used,0,sizeof(used));
        for (int i=1;i<cnt;i++){
            if (used[i]) continue;
            cnt2++;
            for (int j=i+1;j<cnt;j++)
                if (P[i].k==P[j].k)
                    used[j]=1;
        }
        //cout<<cnt2<<endl;
        int cnt3=0;
        memset(used,0,sizeof(used));
        for (int i=1;i<cnt;i++){
            if (used[i]) continue;
            cnt3++;
            for (int j=i+1;j<cnt;j++){
                if (P[i].k!=P[j].k) continue;
                bool flag=0;
                for (int t=0;t<P[i].k;t++){
                    if (P[i].x[t]!=P[j].x[t] || P[i].y[t]!=P[j].y[t]){
                        flag=1;
                        break;
                    }
                }
                if (!flag) used[j]=1;
            }
        }
        //cout<<cnt3<<endl;
        cout<<cnt-1<<" "<<cnt2<<" "<<cnt3<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值