发现大家都是用动归和搜索,我就来一发前缀和!!!
“前缀和可以是数列,也可以是矩阵。
以一个小题为例:给定n个数a[i]以及m个询问并每次询问一段区间的和。要求:一个O(n+m)的做法。
要求效率如此之高,看来,强大的树状数组和线段树也无能为力了。
那么如此一来,前缀和上场了。
若想时间复杂度控制在O(n+m),光读一遍就需要O(n),即每一个询问要求效率为O(1),可怕~~
但前缀和可以做到,开两个数组a[n]和s[n],前者装数据,后者装前n项和。
若想求一段区间[l,r]的和,只需用s[r]-s[l],即可解决。”————摘自CSDN博客 前缀和维护
其实前缀和是一种很方便的东西,所以说本题就可以用前缀和,判定一个正方形是否全是1。
老规矩,首先上变量:
int s[105][105];//记录矩阵的数组 int a[105][105];//记录前缀和的数组 int n,m;//记录矩阵的大小
由于矩阵是二维的,所以前缀和与一维的不太一样
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { a[i][j]=s[i][j]+a[i][j-1]+a[i-1][j]-a[i-1][j-1]; //由于a[i-1][j-1]被加了两遍,所以减回去 }
其实就是该位置到左上方点形成的矩阵中1的个数。
然后就可以三重循环遍历了:
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(s[i][j]!=1) continue;//不是1的不用看了 for(int k=2;i-k>=0&&j-k>=0;++k) { if(a[i][j]+a[i-k][j-k]-a[i-k][j]-a[i][j-k]==k*k)//关键 if(k>ans) ans=k; } }
关键的,其实就是如何计算正方形中1的个数了,这里要注意:因为计算一个矩阵1的个数时,可能会有些问题:
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1(矩阵)
比如计算加粗体的正方形的1的个数
0 0 0 0 0
0 0 1 2 3
0 1 3 5 6
0 1 4 7 8
0 2 6 9 11(前缀和)
大家可能会以为是1+5-2-3=1,很明显不对,其实是1 * 1的矩阵(在5所在的位置)的1的个数
但其实这样的:
0 0 0 0 0
0 0 1 2 3
0 1 3 5 6
0 1 4 7 8
0 2 6 9 11(前缀和)
是0+5-0-1=4,就正确了,就是右下角的所在行列分别减去正方形的边长,再一起减去正方形的边长(就像这样a[i][j]+a[i-k][j-k]-a[i-k][j]-a[i][j-k])就是计算正方形1个数所需的4个点
但很明显,这样的3重循环是会超时的,所以就需要优化:
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(s[i][j]!=1) continue; for(int k=ans+1/*直接遍历比当前答案大的数*/;i-k>=0&&j-k>=0;++k) { if(a[i][j]+a[i-k][j-k]-a[i-k][j]-a[i][j-k]==k*k) ans=k; else break; //如果小的正方形不行了,比他大的但有包含它的正方形肯定不行 } }
最后将程序拼凑在一起加上必要的输入输出,就可以ac了
2018-4-20