题面:
题目描述
给定一个 n ∗ m 个矩阵,矩阵中每个数都是 [1, 12] 内的整数。你可以执行下列两个操作任意多次:
• 指定一行,将该行所有数字 +1.
• 指定一列,将该列所有数字 +1.
如果执行完上述操作之后,矩阵中某个数变成了 3, 6, 9, 12 其中的某一个,我们认为这个数是稳的。
给定初始矩阵,求出任意执行操作之后稳数的最多个数。
输入输出格式
输入格式:
第一行包含两个正整数 n, m。
接下来 n 行,每行 m 个数,描述这个矩阵。
输出格式:
一个整数,表示答案。
输入输出样例
输入样例#1:
3 3
1 2 3
3 2 4
1 2 1
输出样例#1:
7
输入样例#2:
5 5
2 4 6 8 10
1 2 3 4 5
3 4 5 6 7
7 8 9 10 11
5 10 12 3 7
输出样例#2:
20
说明
测试点编号 限制与约束
1 n,m<=2
2 n,m<=5
3~10 n,m<=10
这道题其实是一道送分题,出题人为了暗示这一点把数据出的非常奇怪,题意也只是加法,没有减法,还给了两个SPJ,剩下80分就没有给部分分了。(然而我这个菜鸡还是没想出来,只能打O(12n+m*n*m)大暴力水一水QAQ)
这道题骗分思路就是直接暴力枚举每行每列每个数加多少,优化一下,可以发现每行或者每列只用加0或1或2这3个数,因为既然加这3个数就可以达到目的,就没必要再加更大的数字,避免超出12的范围,这样一来复杂度就变成O(3n+m*n*m)但并没有什么*用。
但只要我们从枚举这里下手优化,就可以发现一个奇妙的方法,使得时间复杂度变成O(3n+1*n*m)。
正解思路:
我们可以先确定每行(或者每列,这个由自己决定,我是先确定每行)要加多少,再枚举每列(每行)加的数,选取其中的最优值,加入答案,输出即可。
代码如下:
#include<bits/stdc++.h> using namespace std; #define FOR(l,r,i) for(int i=l;i<=r;++i) const int N=11; int n,m,ans,a[N][N],c[N][3],d[N];//a[i][j]代表第i行第j列的数字 //c[j][k]代表第j列加上k后满足条件的答案,d[i]代表第i行要加的数。 template<class T>inline void read(T &x)//读入优化 { x=0;int f=0;char ch=getchar(); while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } x=f ? -x:x; return; } inline int check(int x)//判断此时被更改过的点是否越界,是否满足“稳”的条件。 { return x<=12&&(x%3==0?1:0); } void DFS(int x) { if(x>n)//已确定所有行的值,开始扫整张图 来确定答案。 { int res=0; FOR(1,m,j)//枚举列。 { c[j][0]=c[j][1]=c[j][2]=0;//初始化。 FOR(1,n,i)FOR(0,2,k)//i代表行,k代表加的数。 c[j][k]+=check(d[i]+a[i][j]+k);//判断。 res+=max(max(c[j][0],c[j][1]),c[j][2]);//选出其中满足条件最多的那一个。 } ans=max(ans,res);//加入到答案中。 return; } FOR(0,2,i) d[x]=i,DFS(x+1);//确定每行加的值。 return; } int main() { read(n);read(m); FOR(1,n,i)FOR(1,m,j)read(a[i][j]); DFS(1); printf("%d",ans); return 0; }