题目链接:https://nanti.jisuanke.com/t/A1139
题意:给一个n*m的01矩阵,1表示有炸弹,0表示没有,引爆一个炸弹后这个炸弹所在的行和列的炸弹都会被引爆,问最少要手动引爆多少个炸弹可以将所有的炸弹全部引爆。
思路:开始想用dfs直接暴力,结果运行超时,有三组样例没过。后来百度搜了一下,发现这题要用并查集解。首先我没考虑一下,如果一个炸弹,那他所在行和列都会被引爆,那我们就可以把这个炸弹所在的x,y坐标连在一起 ,如果接下来有炸弹出现在这一行或者这一列,那我们就可以把没出现的x或者y坐标在和前面的节点连起来就行啦。那就遇到一个很棘手的问题啦,x,y坐标会重复呀,所以我们可以给y坐标加一个偏移量(+n)那这个问题就可以解决啦。最后直接查询一下就行啦。
AC代码
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1010;
char a[maxn][maxn];
int f[maxn<<1];
int vis[maxn<<1];
void init(int n,int m)
{
for(int i=0; i<n+m; i++)
{
f[i]=i;
vis[i]=0;
}
}
int Find(int x)
{
if(x==f[x]) return x;
else return f[x]=Find(f[x]);
}
void Merge(int x,int y)
{
int t1=Find(x),t2=Find(y);
if(t1!=t2)
{
f[t1]=t2;
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=0; i<n; i++) scanf("%s",a[i]);
init(n,m);
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
if(a[i][j]=='1')
{
Merge(i,j+n);
}
}
}
int ans=0;
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
if(a[i][j]=='1')
{
int t=Find(i);
if(!vis[t]) vis[t]=1,ans++;
t=Find(j+n);
if(!vis[t]) vis[t]=1,ans++;
}
}
}
printf("%d\n",ans);
}
return 0;
}