luogu P4961 小埋与扫雷

题目背景

小埋总是在家中打游戏,一天,她突然想玩Windows自带的扫雷,在一旁的哥哥看见了,想起了自己小时候信息课在机房玩扫雷的日子,便兴致勃勃地开始教小埋扫雷。然而,小埋还是不明白 每局将所有非雷的方块点开所需最少左键点击数,参见扫雷网的教程 )怎么算,于是她找到了你。

题目描述

小埋会告诉你一盘扫雷,用一个n×m 的矩阵表示,1 是雷 ,0 不是雷,请你告诉她这盘扫雷的 3bv 。

周围八格没有“雷”且自身不是“雷”的方格称为“空格”,周围八格有“雷”且自身不是“雷”的方格称为“数字”,由“空格”组成的八连通块称为一个“空”。 3bv= 周围八格没有“空格”的“数字”个数++“空"的个数。

如果看不懂上面的计算方式,可以看题目背景中给出的教程,或者看下面的样例解释。

注:八连通

输入输出格式

输入格式:
第一行有两个整数 n 和 m,代表这盘扫雷是一个 n×m 的矩阵。

后面的 n 行每行有 m 个整数,表示这个矩阵,每个数字为 0 或1,1 代表是雷,0 代表不是雷。

输出格式:
一个整数,代表这盘扫雷的 3bv 。

输入输出样例

输入样例#1: 复制
8 8
0 0 0 1 1 0 0 0
1 0 0 1 0 0 0 1
1 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
输出样例#1: 复制
13
说明

1≤n, m≤1000

 首先解释一下3bv,一个点会显示出四周8个点中有几个雷,对于一个3bv即为值为0的点联通块(同时联通块周围的所有点也视为在这个3bv内)或一个不属于任一联通块的一个四周有雷的点,(1,1)(1,2)(1,3)(2,2)(2,3)........这些都是
这样其是很容易就可以联想到一个很经典的问题填充颜色, 对于零的联通块进行dfs填充颜色,在填充的时候可以可以记录块的个数,最后剩下的块就是雷与距离3bv所差的块数,所以可以减去3bv = 0的联通块数 + n*m - 联通块一共所占的块数(包括0的联通块周围的那些块) - 雷的数目;这个问题就这样解决了。

贴代码:


#include<iostream>
#include<cstdio>
using namespace std;
inline int read(){
    int x = 0;int f = 1;char c = getchar();
    while(c<'0'||c>'9'){
        if(c == '-')f = -f;
        c = getchar();
    }
    while(c<='9' && c>='0'){
        x = x*10 + c - '0';
        c = getchar();
    }
    return x*f;
}
int n,m,ans,tot,cnt;
int pic[2000][2000];
void dfs(int x,int y){
    if(x <= n&&x >= 1 && y <= m&& y >= 1&&pic[x][y] == 0){
       pic[x][y] = -1;
       cnt++;
       dfs(x+1,y);
       dfs(x,y+1);
       dfs(x,y-1);
       dfs(x-1,y);
       dfs(x + 1,y+1);
       dfs(x + 1,y-1);
       dfs(x-1,y-1);
       dfs(x-1,y+1);
    }
    else if(x <= n&&x >= 1 && y <= m&& y >= 1&&pic[x][y]==9&&(pic[x-1][y]==-1||pic[x+1][y]==-1||pic[x][y+1]==-1||pic[x][y-1]==-1)){
        pic[x][y] = -1; 
        cnt++;
    }
}
int main(){
    n = read(); m = read();
    for(int i = 1; i<= n;i++) {
        for(int j = 1; j<=m; j++){
            pic[i][j] = read();
            if(pic[i][j] == 1)tot++;
        }
    }
    for(int i = 1; i<=n; i++){
        for(int j = 1; j<=m; j++){
            if(pic[i][j] == 1){
                if(pic[i+1][j] != 1)pic[i+1][j] = 9;
                if(pic[i][j+1] != 1)pic[i][j+1] = 9;
                if(pic[i][j-1] != 1)pic[i][j-1] = 9;
                if(pic[i-1][j] != 1)pic[i-1][j] = 9;
                if(pic[i+1][j+1] != 1)pic[i+1][j+1] = 9;
                if(pic[i+1][j-1] != 1)pic[i+1][j-1] = 9;
                if(pic[i-1][j+1] != 1)pic[i-1][j+1] = 9;
                if(pic[i-1][j-1] != 1)pic[i-1][j-1] = 9; 
            }
        }
    } 
    for(int i = 1; i<=n;i ++){
        for(int j = 1; j<=m; j++){
            if(pic[i][j] == 0){
                dfs(i,j);/*
                for(int  k = 1; k<=n; k++){
                    for(int p = 1; p<=m ;p++){
                        cout<<pic[k][p]<<' ';
                    }
                    cout<<'\n';
                }
                cout<<cnt<<'\n';*/
                ans++;
            }
        } 
    }
    printf("%d ",ans+n*m-cnt-tot);
    return 0;
}

转载于:https://www.cnblogs.com/Euplectella/p/9912232.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值