• 题意
    • 一个n*m的二进制矩阵,上下左右的1联通算一块区域,问有多少个区域。
    • 输入行数 n,和列数 m(1 ≤ n ≤ 2^12, 4 ≤ m ≤ 2^14),题目保证m可以被4整除
    • 输入 n 行,m/4 列的16进制数字,可得一个 m*n 的二进制矩阵(一位16进制数字可以转成4位二进制数字),上下左右连着的1算是一块,问这个二进制矩阵一共有多少块。
  • 题解
    • 显然是一个并查集找连通块的问题
    • 但是有个问题,数据太大,并查集存不下
    • 所以我们考虑滚动并查集
    • 开两行并查集,每次与左边与上边判断能否合并
    • 那么答案就是1出现的次数-合并的次数
  • 代码
    • //#include<bits/stdc++.h>
      #include<algorithm>
      #include <iostream>
      #include   <fstream>
      #include  <cstdlib>
      #include  <cstring>
      #include  <cassert>
      #include   <cstdio>
      #include   <vector>
      #include   <string>
      #include    <cmath>
      #include    <queue>
      #include    <stack>
      #include      <set>
      #include      <map>
      using namespace std;
      #define P(a,b,c) make_pair(a,make_pair(b,c))
      #define rep(i,a,n) for (int i=a;i<=n;i++)
      #define per(i,a,n) for (int i=n;i>=a;i--)
      #define CLR(vis) memset(vis,0,sizeof(vis))
      #define MST(vis,pos) memset(vis,pos,sizeof(vis))
      #define pb push_back
      #define mp make_pair
      #define all(x) (x).begin(),(x).end()
      #define fi first
      #define se second
      #define SZ(x) ((int)(x).size())
      typedef pair<int,pair<int,int> >pii;
      typedef long long ll;
      typedef unsigned long long ull;
      const ll mod = 1000000007;
      const int INF = 0x3f3f3f3f;
      const int N=1<<14|5;
      char s[N];
      int n,m,ans=0,f[N<<1],g[N];
      inline int trans(const char&x) {
      	return isdigit(x)?x-'0':x-'A'+10;
      }
      inline int getf(int x) {
      	if(x<0||x>m-1)return 0;
      	return (trans(s[x/4])>>(3-x%4))&1;
      }
      inline int find(const int&x) {
      	return x!=f[x]?f[x]=find(f[x]):x;
      }
      inline void merge(int x,int y) {
      	int fx=find(x),fy=find(y);
      	fx!=fy?f[fx]=fy,--ans:0;
      }
      int main() {
      	scanf("%d%d",&n,&m);
      	memset(f,-1,sizeof(f));
      	rep(i,0,n-1){
      		scanf("%s",s);
      		rep(j,0,m-1)g[j]=getf(j);    //获得二进制数位 
      		rep(j,0,m-1)g[j]?++ans,f[j+m]=j+m:f[j+m]=-1;//计算1的个数,并初始化并查集的第二层 
      		rep(j,0,m-1){
      			if(!g[j])continue;//0的话不需要与上层以及左边合并 
      			if(~f[j])merge(j,j+m);//只要并查集第一层该位置的值不是-1,那么该位置就是1,合并第一层入第二层 
      			if(getf(j-1))merge(j+m-1,j+m);//如果左边是1,那么合并左边1入右边 
      		}
      		rep(j,0,m-1){//将第二层转化为第一层,滚动 
      			if(g[j]) 
      				f[j]=find(j+m)-m;//-m是因为我们用是否小于m作为区别第一层与第二次的标志,现在滚动,就要让其小于m 
      			else     
      				f[j]=-1;
      		}
      	}
      	cout<<ans<<endl;
      	return 0;
      }

发表评论

邮箱地址不会被公开。 必填项已用*标注