思维+并查集 hdu5652

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5652

题意:

  输入T,接下来T个样例,每个样例输入n,m代表图的大小,接下来n行,每行m个数,代表图,0表示这个位置可以走,1表示走不了,接下来q个点的位置,表示第q个时间这个点的值变成1,就不能走了,问在什么时间开始从最上面无法走到最下面。

最后还是看了别人博客(不争气啊),用并查集的思路就是先给每个点编号,然后用两个数组pre1,pre2数组方便存储编号为i的点所在的点集(并查集)里面最左和最右的值,当最左的值为0,最右的值为m-1是说明这个连续的点集已经把上下隔断了。只是大致说了下,看代码,有人也用了二分+BFS,对q个时间段进行二分,用BFS判断上下是否被隔断,应该是这样,没用这种方法写。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque> 
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 505
int n,m,k,t,ans,id;
char str[maxn][maxn];
int num[maxn][maxn];//记录点str[i][j]对应的编号 
int pre1[maxn*maxn],pre2[maxn*maxn];//两个数组记录祖先,祖先编号对m取模得出的值就是左右两边最值 
int dir[8][2]={0,1,0,-1,1,0,-1,0,1,1,1,-1,-1,1,-1,-1};//八个方向 
int find1(int a) 
{
    if(a==pre1[a])
    return a;
    return pre1[a]=find1(pre1[a]);
}
void combine1(int x,int y)
{
    int a=find1(x);
    int b=find1(y);
    if(a%m<b%m)//祖先编号对m取模值较小的为合并之后的祖先 
    pre1[b]=a;
    else
    pre1[a]=b;
}
int find2(int a)
{
    if(a==pre2[a])
    return a;
    return pre2[a]=find2(pre2[a]);
}
void combine2(int x,int y)
{
    int a=find2(x);
    int b=find2(y);
    if(a%m>b%m)//祖先编号对m取模值较大的为合并之后的祖先 
    pre2[b]=a;
    else
    pre2[a]=b;
}
void bianhao()
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            num[i][j]=++id;//给每个点编号 
            pre1[id]=id;
            pre2[id]=id;
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(str[i][j]=='1')
            {
                for(int k=0;k<8;k++)//搜索每个黑块周围 
                {
                    int a=i+dir[k][0];
                    int b=j+dir[k][1];
                    if(a>=0&&a<n&&b>=0&&b<m&&str[a][b]=='1')
                    {
                        combine1(num[i][j],num[a][b]);
                        combine2(num[i][j],num[a][b]);
                    }
                }
            }
        }
    }
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        ans=-1;
        id=-1;
        for(int i=0;i<n;i++)
        scanf("%s",str[i]);
        bianhao();
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                int a=find1(num[i][j]);
                int b=find2(num[i][j]);
                if(a%m==0&&b%m==m-1)//在山峰升起前可能两国可能已经隔断 
                {
                    ans=0;
                    break;
                }
            }
            if(ans!=-1)
            break;
        }
        int q;
        scanf("%d",&q);
        for(int s=1;s<=q;s++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            str[x][y]='1';//山峰升起 
            if(ans!=-1)
            continue;
            for(int i=0;i<8;i++)
            {
                int a=x+dir[i][0];
                int b=y+dir[i][1];
                if(a>=0&&a<n&&b>=0&&b<m&&str[a][b]=='1')
                {
                    combine1(num[x][y],num[a][b]);
                    combine2(num[x][y],num[a][b]);
                    int l=find1(num[x][y]);
                    int r=find2(num[x][y]);
                    if(l%m==0&&r%m==m-1)//黑色块所在点集的左右两最值达到左右边界 
                    {
                        ans=s;
                        break;
                    }
                }
            }    
        }
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/6262369sss/p/9818480.html