Mine Layer(2008 World Final C)
题意
类似于扫雷游戏,在一些格子中散布着一些地雷,具体的埋藏位置并不清楚,但知道每个格子及其周围八个格子的地雷总数。请问此时正中间那一行最多可能有多少地雷(题目假定所有的输入都是奇数行的)?
输入:
第一行,有一个正整数N,代表有N个测试数据。
每个测试数据的第一行包含两个正整数R(行数,R是奇数)和C(列数)。以下是R行每行C列的正整数。
输出:
每个测试数据输出一行,“Case #X: Y”,x为第几个测试数据,Y为正中间那一行最多可能有的地雷数。
限制条件
输入有R行C列
3≤R≤49,R是奇数
3≤C≤49
预备内容
我们先考虑一维的情况。
题意
每个格子内有一个数字,具体数字不详,但是知道这个格子和左右两个相邻格子内的数字之和,求问正中间的格子中的数字最大值。
粗略考虑肯定是个搜索(范围到49必然会爆T)或者dp,但是其实是个规律题。
按照模三的余数分类讨论
- 余数为一,可以算出所有格子的数字之和,再算出除了正中央格子以外所有格子的数字之和,很明显可以看出。
- 余数为二,同样可以很简单的算出除了正中央格子以外所有格子的数字之和。
- 余数为零,可以把正中央的格子算两次,再减去总数。
再把上面的结论推广到二维
利用这种方法算出每行和上下两行的地雷总数,相减就能得答案了。
Attention
这题的答案其实是唯一的,但是题面要求最大值,是一种误导。
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<utility>
#include<queue>
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-1-i];
}
}
else if(n%3==2){
res=total(a,n);
for(int i=0;i<n/2;i+=3){
res-=a[i];
res-=a[n-1-i];
}
}
else{
res=0;
for(int i=0;i<n/2;i+=3){
res+=a[i];
res+=a[n-1-i];
}
res-=total(a,n);
}
return res;
}
int main()
{
freopen("C-large-practice.in","r",stdin);
freopen("C-large-practice.out","w",stdout);
int t,kase=0;
scanf("%d",&t);
while(t--){
scanf("%d%d",&R,&C);
for(int i=0;i<R;i++){
for(int j=0;j<C;j++){
scanf("%d",&A[i][j]);
}
}
int row[MAX_RC];
for(int i=0;i<R;i++){
row[i]=total(A[i],C);
}
int ans=center(row,R);
printf("Case #%d: %d\n",++kase,ans);
}
return 0;
}