hrbust 1662 凸多边形【最短路SPFA+建图+思维】好题

本文介绍了一种通过构建图模型并运用最短路径算法来判断二维网格中黑色方格是否构成凸多边形的方法。该算法首先定义了边权表示拐弯次数,接着利用SPFA算法计算所有黑色方格间的最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

凸多边形
Time Limit: 2000 MSMemory Limit: 32768 K
Total Submit: 61(17 users)Total Accepted: 24(17 users)Rating: Special Judge: No
Description

   有一个n x m 的方格阵。开始时,所有的方格的颜色都是白色的。小z已经把其中的一些方格(至少1个)涂成了黑色。如果在方格中从任意一个黑色方格沿着黑色方格走到另一个黑色方格最多只需要拐弯一次的话,我们就称黑色方格组成区域为凸多边形。如下图所示,左边是凸多边形而右边的不是.


现在你需要判断一个方格阵中黑色的格子组成的图形是不是凸多边形。


Input

多组测试数据。

每组测试数据第一行输入两个整数nm表示方格阵的行数和列数(1<=n,m<=50,接下来的n行每行是一个长度为m由‘W’或者‘B’组成的字符串,表示方格的状态分别为白色或者黑色。

Output

对于每组数据,如果图形是凸多边形则输出”YES”,否则输出”NO”。每组输出占一行。

Sample Input
3 4
WWBW
BWWW
WWWB
3 1
B
B
W

Sample Output
NO
YES

Author
`Wind @hrbust

思路:

看到好多人用暴力判断写的,好复杂- -。


1 、我们将拐弯次数设定为边权,将不用拐弯的两点间建立一条边,边权值设定为1.那么如果从某个点i,到某个点j的最短路长度为2,那么说明从点i到点j至少需要拐弯一次,同理,如果某点i到某点j的最短路长度为3,那么说明点i到点j至少需要拐弯两次。那么根据这个特性,我们将每两个黑色的点,必须是这两个点同行或者同列,并且中间没有白色的格子阻拦,那么将这两个点之间建立一条边,边权设定为1.


2、那么我们剩下的任务就是跑contn(表示黑色格子的个数)次单源最短路,如果我们使用nlogn算法的Dij是一定不会超时的,这里的实现还是用的SPFA,(过了就行呗)。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
struct node
{
    int from;
    int to;
    int next;
    int w;
}e[2500000];
char a[150][150];
int num[150][150];
int head[25000];
int vis[25000];
int dis[25000];
int n,m,contn,cont;
void add(int from,int to)
{
    e[cont].to=to;
    e[cont].w=1;
    e[cont].next=head[from];
    head[from]=cont++;
}
void getmap()
{
    contn=0;
    cont=0;
    memset(head,-1,sizeof(head));
    memset(num,0,sizeof(num));
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(a[i][j]=='B')
            {
                num[i][j]=++contn;
            }
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(a[i][j]!='B')continue;
            for(int k=i+1;k<n;k++)
            {
                if(a[k][j]=='B')
                {
                    add(num[i][j],num[k][j]);
                }
                else if(a[k][j]=='W')break;
            }
            for(int k=i-1;k>=0;k--)
            {
                if(a[k][j]=='B')
                {
                    add(num[i][j],num[k][j]);
                }
                else if(a[k][j]=='W')break;
            }
            for(int k=j-1;k>=0;k--)
            {
                if(a[i][k]=='B')
                {
                    add(num[i][j],num[i][k]);
                }
                else if(a[i][k]=='W')break;
            }
            for(int k=j+1;k<m;k++)
            {
                if(a[i][k]=='B')
                {
                    add(num[i][j],num[i][k]);
                }
                else if(a[i][k]=='W')break;
            }
        }
    }
}
int SPFA(int ss)
{
    queue<int >s;
    s.push(ss);
    for(int i=1;i<=contn;i++)dis[i]=0x3f3f3f3f;
    dis[ss]=0;
    memset(vis,0,sizeof(vis));
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
    for(int i=1;i<=contn;i++)
    {
        if(dis[i]==0x3f3f3f3f)return 0;
        if(dis[i]>2)return 0;
    }
    return 1;
}
void Slove()
{
    int flag=0;
    for(int i=1;i<=contn;i++)
    {
        if(SPFA(i)==0)
        {
            flag=1;break;
        }
    }
    if(flag==1)
    {
        printf("NO\n");
    }
    else printf("YES\n");
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%s",a[i]);
        }
        getmap();
        Slove();
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值