【XSY2525】Maze 2017多校

Description

考虑一个 N×M 的网格,每个网格要么是空的,要么是障碍物。整个网格四周都是墙壁(即第1行和第n行,第1列和第m列都是墙壁),墙壁有且仅有两处开口,分别代表起点和终点。起点总是在网格左边,终点总是在网格右边。你只能朝4个方向移动:上下左右。数据保证从起点到终点至少有一条路径。

从起点到终点可能有很多条路径,请找出有多少个网格是所有路径的必经网格。

Input

第一行包含两个整数 N,M ,表示网格 N 行 M 列。

接下来 N 行,每行 M 个字符,表示网格。'#'表示障碍物或墙壁,'.'表示空地。

Output

输出文件包含一个整数,必经点的个数。

Sample Input

7 7
#######
....#.#
#.#.###
#.....#
###.#.#
#.#....
#######

Sample Output

5

HINT

样例解释

(2, 1) (2, 2) (4, 4) (6, 6) (6, 7)

数据范围与约定

对于10%的数据, 3≤N,M≤50

对于50%的数据, 3≤N,M≤500

对于所有数据, 3≤N,M≤1000

前置知识——tarjan算法

看一下题。

题目中说了,要求所有路径的必经网格,这种描述好像跟某一种算法要求的结果相同,是什么呢?

——割点。

我们想一下,如果一个点是所有路径的必经网格,那么如果我们去掉这个点会怎么样?

——起点和终点不联通了。

这就很像我们的割点和割边了。

在一个连通图中,如果我们去掉一个点或一条边,使得剩下的图变得有一部分不连通,那么这个点或这条边就被称为割点(边)。

这是不是很像这道题。

那我们就说一下这道题的解法。

我们枚举每一个不为‘#’的点,每个点与他周围不为‘#’的边连边,然后求出这个图的割点数(不算起点和终点),把答案加2(起点和终点),输出。

是不是觉得有点道理?

但是,这样做有很大的错误。

看一下这张图

1736301-20190815212452620-1025595849.png

如图,按照上面这种算法,涂红色的点都应该是答案,但依照题目,只有最上面的红点和起(终)点是答案。

所以,单纯的求割点是不能的到正确答案的。

我们应该在tarjan中加一个判断这段路的末尾是否是终点,我们才把这段路上的割点设为答案。

#include<bits/stdc++.h>
#define N 1000000
using namespace std;
int nct,cnt,number[N],ans,dfn[N],low[N],indew,to[N*20],head[N],nxt[N*20],yes[N],root,n,m,is[1001][1001],f[4][2]={{1,0},{0,1},{-1,0},{0,-1}},ed;
char ch[2001];
bool viss[N];
void adde(int u,int v)
{
    to[++nct]=v;
    nxt[nct]=head[u];
    head[u]=nct;
}
bool tarjan(int u,int fa)
{
    int child=0;
    dfn[u]=low[u]=++indew;
    bool flag=0;//u这条路的末尾是否是终点
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa)
        {
            continue;
        }
        bool fflag=0;//v这条路的末尾是否是终点
        if(!dfn[v])
        {
            if(tarjan(v,u))
            {
                flag=fflag=1;
            }
            if(low[v]>=dfn[u]&&fflag)
            {
                yes[u]=1;//是答案的割点
            }
            low[u]=min(low[v],low[u]);
            child++;
        }else{
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(fa==-1&&child<2)
    {
        yes[u]=0;
    }
    return flag||(u==ed);//回溯
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch);
        for(int j=0;j<m;j++)
        {
            if(ch[j]=='.')
            {
                is[i][j+1]=1;
            }
        }
    }
    for(int i=2;i<n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            for(int k=0;k<=3;k++)
            {
                int xx=i+f[k][0],yy=j+f[k][1];
                if(!is[xx][yy]||!is[i][j])
                {
                    continue;
                }
                if(!viss[(i-1)*m+j])//编号
                {
                    number[(i-1)*m+j]=++cnt;
                    viss[(i-1)*m+j]=1;
                }
                if(!viss[(xx-1)*m+yy])
                {
                    number[(xx-1)*m+yy]=++cnt;
                    viss[(xx-1)*m+yy]=1;
                }
                if(j==1&&!root)//求起点
                {
                    root=number[(i-1)*m+j];
                }
                if(yy==1&&!root)
                {
                    root=number[(xx-1)*m+yy];
                }
                if(j==m&&!ed)//求终点
                {
                    ed=number[(i-1)*m+j];
                }
                if(yy==m&&!ed)
                {
                    ed=number[(xx-1)*m+yy];
                }
                adde(number[(i-1)*m+j],number[(xx-1)*m+yy]);//连边
            }
        }
    }
    tarjan(root,-1);//tarjan求割点
    for(int i=1;i<=cnt;i++)
    {
        if(yes[i])
        {
            ans++;
        }
    }
    ans+=2;
    printf("%d\n",ans);
    return 0;
}
/*
7 7
#######
....#.#
#.#.###
#.....#
###.#.#
#.#....
#######
*/

转载于:https://www.cnblogs.com/2017gdgzoi44/p/11360542.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值