持续更新中😬 加个关注,后续上新不错过~
一 、问题描述
类似于扫雷游戏,在一些格子中散布着一些地雷,具体的埋藏位置并不清楚,但是知道每个格子及周围八个格子的地雷的总数。请问此时正中间那一行最多可能有多少地雷?(题目假设所有的输入都是奇数行的)
⚠️限制条件
- 输入有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;
}
四 、输入输出样例
若有帮助的话,请点个赞吧!😊