ACM入门-DFS、BFS、并查集与图的连通性

zzx最近在玩一款名为风来之国的游戏,这是一款国产像素冒险游戏,

他操纵着男主角约翰以及女主角小珊在迷宫中冒险,

而现在,zzx来到了一个 关卡,这是一个特殊的迷宫

迷宫由01矩阵构成,总共有n*n个格子,zzx操作的角色只能往相邻的4个格子移动,

迷宫会在zzx操作的角色移动一格后自动帮助zzx切换到另一个角色,并且zzx不能自己切换角色

约翰只能走位置为1的矩阵,而小珊只能走位置为0的矩阵,

zzx想知道,从某个点出发,他能达到的格子的数量(启始位置也算一个格子)。

Input

第一行输入一个n(1 <= n <= 50),

下面n行,每行n个字符,字符只可能为0或1,中间无空格

接下来一行输入两个整数 x,y(1<= x ,y <= 50),表示启始位置

tip : 若启始位置为1则表示启始角色为小珊,否则启始角色为约翰

Output

输出一个整数,表示zzx操作的角色能移动到多少个格子上

SampleInput

5
10101
01010
10101
01010
10101
2 5

SampleOutput

25

问题模型分析

输入给出一个n*n的图,判断从某个点出发能到达的格子的最大数目,常见问题会给出一些障碍来将区域围起来从而将整张图分隔成几个封闭图,如以下数据,1为障碍物,0为平地,则从(2,5)出发可以移动到(3,4),(3,5)其他地方将会被围住。
10011
11110
10100
01011
10101

而在该题中依然有障碍物的概念,当你为约翰时障碍物为0,当你为小珊时障碍物为1,他们交替出现所以我们还需要当前角色的身份信息。
常见的求连通块面积的方法有DFS与BFS下面详细介绍DFS写法

#include<bits/stdc++.h>
using namespace std;
int ans=1,n;
int mp[55][55],book[55][55],st[55][55];//标记数组
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};//方向数组
void dfs(int x,int y,int v)//当前身份用v来记录,以及当前坐标
{

     if(ans==n*n)return;//剪枝
    for(int i=0;i<4;i++)
    {
         int nx=x+dx[i],ny=y+dy[i];
         if(nx<1||nx>n||ny<1||ny>n)continue;//越界
         if(mp[nx][ny]==v)continue;//判断障碍
         if(st[nx][ny])continue;
          ans++;
         st[nx][ny]=1;
         dfs(nx,ny,(v+1)%2);//可知身份为0->1->0这样的规律转变,正好为奇偶性
   
    }
}
int main()
{
    cin>>n;
    string s;
    for(int i=1;i<=n;i++)//读入地图
    {
        cin>>s;
        for(int j=1;j<=n;j++)
            mp[i][j]=s[j-1]-'0';
    }
    int x,y;
    cin>>x>>y;
    st[x][y]=1;
    dfs(x,y,mp[x][y]);
    cout<<ans;

}

进阶

Input

同样的一个问题,如果加大数据范围那么问题就会变得更加复杂,我们给出以下输入范围

第一行输入两个整数n, m(1 <= n <=1000, 1 <= m <= 100000),

下面n行,每行n个字符,字符只可能为0或1,中间无空格

接下来m行, 每行输入两个整数 x,y(1<= x ,y <= 1000),表示启始位

时间复杂度分析

上一题只需要求一个点所在的联通块,而这一题至多需要求100000的点的连通块面积,那么如果按照之前每次动用DFS函数,由于DFS的时间复杂度不太好计算,但是根据经验通常是指数级别的复杂度,必定会超时。对于多次询问,常见的优化是预处理,那么该题如何优化呢?
假设询问的点在同一个连通块中,那么他们答案相同。那么我们可以预处理出所有的连通块并记录该块为第i块连通块,记录出第i块的连通块面积。同样给出DFS代码。

#include<bits/stdc++.h>
using namespace std;
int n,maxn=1;
int mp[1005][1005],st[1005][1005],ans[1000005];
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
void dfs(int x,int y,int v,int cnt)//求一点的连通块面积
{

    for(int i=0;i<4;i++)
    {
         int nx=x+dx[i],ny=y+dy[i];
         if(nx<1||nx>n||ny<1||ny>n)continue;
         if(mp[nx][ny]==v)continue;
         if(st[nx][ny])continue;
         if(!st[nx][ny])
         {
             st[nx][ny]=cnt;
             maxn++;
         }
         dfs(nx,ny,(v+1)%2,cnt);
    }

}
int main()
{   int m;
    cin>>n>>m;
    string s;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        for(int j=1;j<=n;j++)
            mp[i][j]=s[j-1]-'0';
    }
    int cnt=0;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
        maxn=1;
        if(!st[i][j])//该点未被走过
        {    st[i][j]=++cnt;
            dfs(i,j,mp[i][j],cnt);
        }
    }
    for(int i=1;i<=n;i++)//这时候整张图被染色成功为不同的数字,相同的数字为同一个连通块,则该连通块面积为这个数字的总数
      {for(int j=1;j<=n;j++)
          ans[st[i][j]]++;
      }

    while(m--)
    {

        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",ans[st[x][y]]);
    }

}

BFS解法

给出BFS代码供大家参考在这里插入图片描述

并查集解法

用并查集来记录是否为同一连通块
在这里插入图片描述

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值