引爆炸弹

在一个 n \times mn×m 的方格地图上,某些方格上放置着炸弹。手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去。

现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆。

输入格式

第一行输两个整数 n,mn,m,用空格隔开。

接下来 nn 行,每行输入一个长度为 mm 的字符串,表示地图信息。0表示没有炸弹,1表示炸弹。

数据约定:

对于 60\%60% 的数据:1 \le n, m \le 1001n,m100;

对于 100\%100% 的数据:1 \le n, m \le 10001n,m1000;

数据量比较大,不建议用cin输入。

输出格式

输出一个整数,表示最少需要手动引爆的炸弹数。

样例输入

5 5
00010
00010
01001
10001
01000

样例输出

2

算法:并查集
思路:将行和列的编号看成是单独的点,i即为i,j=j+n,然后用并查集将行和列加入集合,只有所有‘1’中行和列有一个相同(连炸特性)
的就会在一个集合中,最后并查集的集合个数就是需要的最少的投弹次数

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std ;

const int N = 2010 ;

char g[N/2][N/2] ;
int p[N] ;
bool st[N] ;
int n, m ;

int find(int x){
    if(x != p[x]) p[x] = find(p[x]) ;
    return p[x] ;
}

int main(){
    scanf("%d%d",&n,&m) ;
    for(int i=0;i<n;i++){
        scanf("%s",g[i]) ;
    }
    
    for(int i=0;i<m+n;i++){
        p[i] = i ;
    }
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(g[i][j] == '1'){
                int a = find(i) ;
                int b = find(j+n) ;
                if(a != b) p[a] = b ;
            }
        }
    }
    int cnt = 0 ;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            int a = find(i) ;
            if(!st[a] && g[i][j] == '1'){
                cnt ++ ;
                st[a] = true ;
            }
        }
    }
    cout << cnt << endl ;
    
    return 0 ;
} 

 

 

...

posted @ 2020-03-12 17:26  gulangyuzzz  阅读(159)  评论(0编辑  收藏  举报