GYM 100971 A.Treasure Island(dfs+并查集)

本文介绍了一种算法,用于判断迷宫地图中是否存在一种方式将所有空地连接成一个连通块。通过扫描地图、并查集和深度优先搜索等步骤,最终确定地图的连通状态,并给出具体解决方案。

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

Description
给出一张n*m的地图,点表示空地,井号表示墙,问号不确定,问是否可以把问号变成点或井号使得整张图只有一个连通块
Input
第一行两个整数n和m表示地图规模,之后一个n*m矩形表示该地图(1<=n,m<=50)
Output
如果存在唯一解则输出该唯一解,如果多解输出Ambiguous,如果无解输出Impossible
Sample Input
这里写图片描述
Sample Output
这里写图片描述
Solution
1.首先处理一些必须是#的?,即从每个点开始扫连通块,遇到问号就把问号变成点并标记,最后没有标记的问号就是一定变成#的;
2.然后判无解,即把没有处理的问号全部变成点再扫一遍连通块,用并查集维护下连通性,如果最后超过一个连通块说明无解;
3.然后判多解,即判是否存在一个问号使得将其变成#也能使得只有一个连通块,判的方法很简单,之前只有一个连通块时可以知道处于这个连通块中的点数cnt,现在开始枚举每个问号,把这个问号变成#然后开始扫连通块,如果连通块点数为cnt-1说明这个问号变成#也行,那么就多解了,否则把这个问号变成点接着去找下一个问号;
4.始终不出现多解的情况说明有唯一解
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 2555
char s[55][55],ss[55][55];
int n,m,fa[maxn],num[maxn];
void init()
{
    for(int i=1;i<=n*m;i++)fa[i]=i,num[i]=1;
}
int find(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y)return ;
    fa[y]=x;
    num[x]+=num[y]; 
}
int dx[]={-1,0,1,0};
int dy[]={0,-1,0,1};
int P(int x,int y)
{
    return (x-1)*m+y;
} 
int deal()
{
    init();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(ss[i][j]=='.')
                for(int k=0;k<4;k++)
                {
                    int ii=i+dx[k],jj=j+dy[k];
                    if(ii<1||ii>n||jj<1||jj>m||ss[ii][jj]!='.')continue;
                    unite(P(i,j),P(ii,jj));
                }
    int cnt=0,ans;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(ss[i][j]=='.'&&fa[P(i,j)]==P(i,j))
                cnt++,ans=num[P(i,j)];
    if(cnt>1)return -1;
    return ans;
}
int vis[55][55];
void dfs(int x,int y)
{
    vis[x][y]=1;
    for(int k=0;k<4;k++)
    {
        int ii=x+dx[k],jj=y+dy[k];
        if(ii<1||ii>n||jj<1||jj>m||vis[ii][jj]||ss[ii][jj]=='#')continue;
        dfs(ii,jj);
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                ss[i][j]=s[i][j];
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(!vis[i][j]&&ss[i][j]=='.')
                    dfs(i,j);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(ss[i][j]=='?'&&!vis[i][j])s[i][j]='#';
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                ss[i][j]=s[i][j];
                if(s[i][j]=='?')ss[i][j]='.';
            }
        int cnt=deal();
        if(cnt==-1)printf("Impossible\n");
        else
        {
            int gg=0;
            for(int i=1;i<=n&&!gg;i++)
                for(int j=1;j<=m&&!gg;j++)
                    if(s[i][j]=='?')
                    {
                        ss[i][j]='#';
                        int temp=deal();
                        if(temp==cnt-1)gg=1;
                        ss[i][j]='.';
                    }
            if(gg)printf("Ambiguous\n");
            else 
            {
                for(int i=1;i<=n;i++)
                {
                    for(int j=1;j<=m;j++)
                        printf("%c",ss[i][j]);
                    printf("\n");
                }
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值