十四届蓝桥杯 F题岛屿个数

注意点:

1.怪不得这么多佬都喜欢用字符读01,因为int读得处理一下

当输入是010111串时,用int读,只算一个int

解决方法:scanf("%1d",...),只读入一个整数

2.bfs时,记得每输入一个图,将标记置为false

memset(u,0,sizeof u);  //#include<cstring>

分析

1.不加条件“不包括环内岛屿”,则是经典的求岛屿数量题目,直接dfs or bfs

2.加了条件,本题在按经典求解方法求出后,还需要判断是否是子岛屿

3.如何判断:

在输入图之前,手动增加一圈外海,从外海开始遍历图,那么该岛屿一定不是环内岛屿

BFS求解

从起点开始找:

(1)是海,入队,标记

(2)不是海,标记为岛屿,岛屿数量+1

bfs一般用队列实现

1.经典求岛屿个数代码

void bfs_island(int x,int y) //从起点开始
{
	queue<PII> q;
	q.push({x,y});  //入队
	u[x][y] = true;  //标记已访问
	while(!q.empty())  //队不为空
	{
		PII p = q.front();  //取队首
		q.pop();  //弹出队首
		for(int i = 0;i < 4;i ++)  //遍历上下左右,是否能组成一个岛
		{
			int a = p.first + dx[i];
			int b = p.second + dy[i];
			if((a>=1&&a<=m) && (b>=1&&b<=n) && g[a][b]==1 && !u[a][b])  //未超出边界,未访问,且是岛屿(中的一个点)
			{
				q.push({a,b});  //入队
				u[a][b] = true;  //标记已访问
			}
		}
	}
}

2.本题解法

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;

const int N = 55;
typedef pair<int,int> PII;
bool u[N][N];
int g[N][N];
int t,n,m;

int dx[] = {1,-1,0,0,1,-1,-1,1};
int dy[] = {0,0,-1,1,1,1,-1,-1};

//遍历岛屿
void bfs_island(int x,int y)
{
	queue<PII> q;
	q.push({x,y});
	u[x][y] = true;
	while(!q.empty())
	{
		PII p = q.front();
		q.pop();
		for(int i = 0;i < 4;i ++)
		{
			int a = p.first + dx[i];
			int b = p.second + dy[i];
			if((a>=1&&a<=m) && (b>=1&&b<=n) && g[a][b]==1 && !u[a][b])
			{
				q.push({a,b});
				u[a][b] = true;
			}
		}
	}
}

//遍历外海
int bfs_sea(int x,int y)
{
	int cnt = 0;  //记录岛屿数量
	queue<PII> q;
	u[x][y] = true;
	q.push({x,y});
	while(!q.empty())
	{
		PII p = q.front();
		q.pop();
		for(int i = 0;i < 8;i ++)  //遍历海要八个方向
		{
			int a = p.first + dx[i];
			int b = p.second + dy[i];
			if((a>=0&&a<=m+1) && (b>=0&&b<=m+1) && !u[a][b])  //未出界,未访问
			{
				if(g[a][b] == 0)  //是海,入队,标记已访问
				{
					q.push({a,b});
					u[a][b] = 1;
				}
				else  //是陆地,找岛屿(将同属于一个岛屿的点标记为true),数量+1
				{
					bfs_island(a,b);
					cnt ++;
				}
			}
		}
	}
	return cnt;
}

int main()
{
	scanf("%d",&t);
	while(t --)
	{
		memset(u,0,sizeof u);  //每次输入图将标记置false
		scanf("%d %d",&n,&m);
        
        //手动添加一圈外海
		for(int i = 0;i < m + 2;i ++)
		{
			for(int j = 0;j < n + 2;j ++)
			g[i][j] = 0;
		}
        //输入图
		for(int i = 1;i < m + 1;i ++)
		{
			for(int j = 1;j < n + 1;j ++)
			scanf("%1d",&g[i][j]);
		}
		int cnt = bfs_sea(0,0);  //找岛屿数量
		printf("%d\n",cnt);
	}
	return 0;
}

DFS求解

1.标记所有海

2.遍历图:

(1)从外海来的,且该点是岛屿,cnt ++

(2)不是,说明是子岛屿,不做处理

dfs可用栈和递归

1.经典求岛屿个数代码

//递归
void dfs_island(int x,int y)
{
    if(x<1||x>m+1||y<1||y>n+1) return;  //超出边界,返回
    if(g[x][y] == 1)  //是岛屿,接着找其上下左右的点
    {
        g[x][y] = 0;  //1:岛屿;2:外海;0:内海
        for(int i = 0;i < 4;i ++) dfs_island(x+dx[i],y+dy[i]);
    }
    return ;
}

2.本题目解法

#include<iostream>
using namespace std;

const int N= 55;
int dx[8]={-1,1,0,0,1,-1,-1,1},dy[8]={0,0,-1,1,1,1,-1,-1};
int g[N][N];
int t,n,m;

//置外海为2
void dfs_sea(int x,int y)
{
    if(x<0||x>m+2||y<0||y>n+2) return;  //超出边界
    if(g[x][y] == 0)
    {
        g[x][y] = 2;
        for(int i = 0;i < 8;i ++) dfs_sea(x+dx[i],y+dy[i]);  //八个方向
    }
    return;
}

//置岛屿为0
void dfs_island(int x,int y)
{
    if(x<1||x>m+1||y<1||y>n+1) return;
    if(g[x][y] == 1)
    {
        g[x][y] = 0;
        for(int i = 0;i < 4;i ++) dfs_island(x+dx[i],y+dy[i]);
    }
    return ;
}

int main()
{
    scanf("%d",&t);
    while(t --)
    {
        scanf("%d %d",&m,&n);
        //手动添加一圈海
        for(int i = 0;i < m + 2;i ++)
        {
            for(int j = 0;j < n + 2;j ++)
            g[i][j] = 0;
        }
        //读入图
        for(int i = 1;i < m + 1;i ++)
        {
            for(int j = 1;j < n + 1;j ++)
            scanf("%1d",&g[i][j]);  //划重点!!题目是以字符的形式输入,想用int读一位整数——%1d
        }
        dfs_sea(0,0);
        int cnt = 0;
        //遍历图
        for(int i = 1;i < m + 1;i ++)
        {
            for(int j = 1;j < n + 1;j ++)
            {
                if(g[i][j] == 1 && g[i-1][j] == 2)  //是岛屿且是可以是外海来的
                {
                    cnt ++; //岛屿数量+1
                    dfs_island(i,j);  //找到一个岛将其所有点置0
                }
            }
        }
        printf("%d\n",cnt);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值