Mine Layer


持续更新中😬 加个关注,后续上新不错过~


一 、问题描述

类似于扫雷游戏,在一些格子中散布着一些地雷,具体的埋藏位置并不清楚,但是知道每个格子及周围八个格子的地雷的总数。请问此时正中间那一行最多可能有多少地雷?(题目假设所有的输入都是奇数行的)

在这里插入图片描述

⚠️限制条件

  • 输入有R行C列

Small

  • R=3,5
  • 3≤C≤5

Large

  • 3≤R≤49,R是奇数
  • 3≤C≤49



样例
输入


R = 3,C = 3
各个格子的信息如下:
2 2 1
3 4 3
2 3 2


输出


1(地雷的分布只有右图一种)
在这里插入图片描述



二 、分析

1、穷竭搜索

稍加思索便会发现要妥善处理地雷的影响并不容易,要应用动态规划之类的方法比较困难。
于是首先想到的解法便是穷竭搜索。利用递归函数,从左上角开始,枚举每个格子有地雷或者没有地雷。但是,Large的输入规模行列都达到了49,要用这个算法解决是很困难的。

2、一维的情况

首先让我们从如下简化的一维版的问题开始考虑。每个格子内有一个数字,但具体数字不详。但知道该格子和左右两个相邻的格子内的数字之和(下面称这个和为信息)。
在这里插入图片描述
那么,这时正中央格子中的数字最大是多少呢?

3、按模3的余数分类讨论

首先让我们来考虑一下上面的样例。把下图灰色格子的信息加起来,就可以得到所有格子的数字之和。
在这里插入图片描述

而把下图灰色格子的信息加起来,就得到了除正中央格子以外所有格子的数字之和。
在这里插入图片描述

将这两个结果相减,就可以推出正中央的数字为(4+8+5)-(8+6)= 3。



当长度模3余1时,我们都可以这样计算得到正中央的数字。例如,对于长度为13的情况,就可以像下图这样计算。注意比上面的例子更大的模3余1的长度应该是13,而不是10,因为长度应该是奇数,这样才存在正中央的格子。
在这里插入图片描述



同样,当长度模3余2时,也可以像下图一样,求出两种和并相减,得到正中央的数字。
在这里插入图片描述



另一方面,当长度模3余0时,无法直接计算除正中央格子以外所有格子的数字之和。但是,可以像下图一样,求出重复计算了正中央格子两次的和,再通过与全体之和相减得到正中央的数字。
在这里插入图片描述


4、推广到二维

原问题是二维的,但不会带来什么困难。只要先用前面介绍的方法对各行算出全体之和,我们就可得知每行及其上下两行所含的地雷总数。然后再对各行的全体之和运用一维版问题的方法,就可以求得正中间那一行地雷的个数了。
在这里插入图片描述

(这里可能有人会感到奇怪了,我们明明在讨论“当长度模3余1”时,提到了长度应该为奇数,在上面这个例子中,明明每行是偶数(4个)。这里我们需要注意,上面指的是一维每行中间数的值,而在二维中我们求的是中间那一行总的和。转化成一维后,再求解最中间数的值。)


5、本题的陷阱

本题要求最大值,但其实值是唯一的,这是本题设置的一个大陷阱。写成最大值就是想引选手上当,而事实上值只有一个。尤其是对于那些知道扫雷这类游戏没有简单算法可解的选手来说,他们很容易想偏,从而忽略了这种简单的解法。



三 、 参考代码

#include <iostream>
using namespace std;
#define MAX_RC 50
int R,C;
int A[MAX_RC][MAX_RC];

// 计算长度为n的一维版问题的总和
int total (int *a,int n)
{
    int res=0;
    // 按模3的余数分类讨论
    if(n%3==1||n%3==2){
        for(int i=0;i<n;i+=3){
            res+=a[i];
        }
    }
    else{
        for(int i=1;i<n;i+=3){
            res+=a[i];
        }
    }
    return res;
}

// 计算长度为n的一维版问题正中央的数字
int center(int *a,int n)
{
    int res;
    // 按模3的余数分类讨论
    if(n%3==1){
        res=total(a,n);
        for(int i=1;i<n/2;i+=3){
            res-=a[i];
            res-=a[n-i-1];
        }
    }
    else if (n%3==2){
        res = total(a,n);
        for(int i=0;i<n/2;i+=3){
            res-=a[i];
            res-=a[n-i-1];
        }
    }
    else{
        res=0;
        for(int i=0;i<n/2;i+=3){
            res+=a[i];
            res+=a[n-i-1];
        }
        res-=total(a,n);
    }
    return res;
}

void solve()
{
    // 计算各行的总和
    int rows[49];
    for(int i=0;i<R;i++){
        rows[i]=total(A[i],C);
    }
    
    // 求解一维版问题
    int ans = center(rows,R);
    printf("%d\n",ans);
    
}

int main()
{
    scanf("%d%d",&R,&C);
    for(int i=0;i<R;i++){
        for(int j=0;j<C;j++){
            scanf("%d",&A[i][j]);
        }
    }
    solve();
    return 0;
}



四 、输入输出样例

在这里插入图片描述


若有帮助的话,请点个赞吧!😊

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值