对称子矩形(DEBUG)题解
题目描述
在调试程序时,Mirko注意到程序中的一个错误可能与程序内存中所谓的"方杀手"的存在有关。程序内存是一个由R行和C列组成的矩阵,仅由零和一组成。方杀手是内存中的一个正方形子矩阵,由多个字符组成,当旋转180度时看起来完全相同。例如,以下矩阵包含3个方杀手:
101010
111001
101001
(1)
....10
....01
......
(2)
......
...00.
...00.
(3)
101...
111...
101...
Mirko想知道最大方杀手的大小与程序中的错误是否有关。请帮助Mirko编写一个程序,根据内存的布局输出最大方杀手的大小。方杀手的大小是方杀手包含的行数(或列数)。在上面的例子中,方杀手的大小分别为2、2和3。
样例
样例输入1
3 6
101010
111001
101001
样例输出1
3
样例输入2
4 5
10010
01010
10101
01001
样例输出2
3
样例输入3
3 3
101
111
100
样例输出3
-1
解法
暴力——人类的智慧!
我们可以枚举边长,然后枚举左上角,再判断是否为对称子矩形。
枚举边长:
O
(
n
)
O(n)
O(n)
枚举左上角:
O
(
n
2
)
O(n^2)
O(n2)
判断是否为对称子矩形:
O
(
n
2
)
O(n^2)
O(n2)
总时间复杂度:
O
(
n
5
)
O(n^5)
O(n5)
恭喜你,40分!
优化的开始——二分!
我们可以想想,枚举边长的时候可不可以二分呢?那么,时间复杂度就完美的把一个
O
(
n
)
O(n)
O(n) 降成
O
(
l
o
g
2
n
)
O(log_2^n)
O(log2n)。
枚举边长:
O
(
l
o
g
2
n
)
O(log_2^n)
O(log2n)
枚举左上角:
O
(
n
2
)
O(n^2)
O(n2)
判断是否为对称子矩形:
O
(
n
2
)
O(n^2)
O(n2)
总时间复杂度:
O
(
n
4
l
o
g
2
n
)
O(n^4log_2^n)
O(n4log2n)
虽然不知道有多少分~~(懒得测了)~~,但时间复杂度已经降下来了。
只判断边框——降了一个 O ( n ) O(n) O(n) !
我们在判断的时候会出现多余的地方,用一个 f l a g [ x ] [ y ] [ l e n ] flag[x][y][len] flag[x][y][len] 数组记录以 ( x , y ) (x,y) (x,y) 为左上角,边长为 l e n len len 的矩阵是否为回文串,每次判断的时候如果 f l a g [ x + 1 ] [ y + 1 ] [ l e n − 2 ] flag[x + 1][y + 1][len - 2] flag[x+1][y+1][len−2] 为假,直接跳掉,每次判断的时候只要判断两条长和两条宽就行了。
20230828_095458
枚举边长:
O
(
n
)
O(n)
O(n) 或
O
(
l
o
g
2
n
)
O(log_2^n)
O(log2n)
枚举左上角:
O
(
n
2
)
O(n^2)
O(n2)
判断是否为对称子矩形:
O
(
n
)
O(n)
O(n)
总时间复杂度:
O
(
n
4
)
O(n^4)
O(n4) 或
O
(
n
3
l
o
g
2
n
)
O(n^3log_2^n)
O(n3log2n)
O
(
n
3
l
o
g
2
n
)
O(n^3log_2^n)
O(n3log2n) 接近
O
(
n
3
)
O(n^3)
O(n3) 有100的希望了!
逆流而上——又优化了一点点!
我们可以倒着枚举边长,只要找到了,退出整个循环,有可能优化一点点,但效果不明显~~(卡常用)~~。
几个long long跑得快——二进制拆分!
我们可以每64个数为一组,判断的时候直接
O
(
n
l
o
g
64
n
)
O(nlog_{64}^n)
O(nlog64n),
O
(
l
o
g
64
n
)
O(log_{64}^n)
O(log64n) 直接可以忽略不计!
枚举边长:
O
(
n
)
O(n)
O(n) 或
O
(
l
o
g
2
n
)
O(log_2^n)
O(log2n)
枚举左上角:
O
(
n
2
)
O(n^2)
O(n2)
判断是否为对称子矩形:
O
(
n
l
o
g
64
n
)
O(nlog_{64}^n)
O(nlog64n)
总时间复杂度:
O
(
n
4
l
o
g
64
n
)
O(n^4log_{64}^n)
O(n4log64n) 或
O
(
n
3
l
o
g
2
n
l
o
g
64
n
)
O(n^3log_2^nlog_{64}^n)
O(n3log2nlog64n)
一个矩阵一个数——二维hash!
枚举边长和左上角不能再优化了,但判断的时间复杂度可以降成
O
(
1
)
O(1)
O(1) !二维hash上场!
我们用p1、p2表示两个质数(随便什么质数都可以,不同就行了),第i行j列的数为
a
i
j
×
p
1
i
×
p
2
j
a_{ij} \times p1^i \times p2^j
aij×p1i×p2j,然后矩阵的哈希值就是所有数的和。
H
=
∑
a
i
j
×
p
1
i
×
p
2
j
%
m
o
d
H = \sum a_{ij} \times p1^i \times p2^j \% mod
H=∑aij×p1i×p2j%mod
/*
*/
#include<bits/stdc++.h>
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ull unsigned long long
#define sort stable_sort
#define INF 0x7f7f7f7f
#define ll long long
using namespace std;
ull p1[1010],p2[1010],h1[1010][1010],h2[1010][1010];
int n,m,ans,a[1010][1010];
int main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>m;
int i,j,l;
ull s1,s2;
char ch;
rep(i,1,n,1) rep(j,1,m,1) cin>>ch,a[i][j] = ch - '0';
rep(i,1,n + m,1) p1[i] = 2 * p1[i - 1],p2[i] = 3 * p2[i - 1];
rep(i,1,n,1) rep(i,1,m,1)
h1[i][j] = h1[i - 1][j] + h1[i][j - 1] - h1[i - 1][j - 1] +
p1[i] * p2[j] * a[i][j];
rep(i,1,n,1) rep(i,1,m,1)
h2[i][j] = h2[i + 1][j] + h2[i][j + 1] - h2[i + 1][j + 1] +
p2[n - i + 1] * p2[m - j + 1] * a[i][j];
rep(l,1,min(n,m),1) rep(i,1,n - l,1) rep(j,1,m - l,1){
s1 = h1[i + l][j + l] - h1[i + l][j - 1] - h1[i - 1][j + l] + h1[i - 1][j - 1];
s2 = h2[i + l][j + l] - h2[i + l][j + 1] - h2[i + 1][j + l] + h2[i + 1][j + 1];
if(s1 == s2){
ans = l;
l++;
i = 1,j = 1;
}
}
if(ans > 1) cout<<ans;
else cout<<-1;
return 0;
}
恭喜,18分。
让我们想想,设矩阵的左上角为
(
x
,
y
)
(x,y)
(x,y),我们算出来的矩阵的值会多出
p
1
x
−
1
×
p
2
y
−
1
p1^{x - 1} \times p2^{y - 1}
p1x−1×p2y−1 倍!怎么办?除掉?~~(我不会)~~只有乘。
交叉相乘法
小学知识:
s
1
p
1
x
−
1
×
p
2
y
−
1
=
s
2
p
1
n
−
x
−
l
×
p
2
n
−
y
−
l
\frac{s1}{p1^{x - 1} \times p2^{y - 1}} = \frac{s2}{p1^{n - x - l} \times p2^{n - y - l}}
p1x−1×p2y−1s1=p1n−x−l×p2n−y−ls2
s
1
×
p
1
n
−
x
−
l
×
p
2
n
−
y
−
l
=
s
2
×
p
1
x
−
1
×
p
2
y
−
1
s1 \times p1^{n - x - l} \times p2^{n - y - l} = s2 \times p1^{x - 1} \times p2^{y - 1}
s1×p1n−x−l×p2n−y−l=s2×p1x−1×p2y−1
所以,代码来——
/*
*/
#include<bits/stdc++.h>
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ull unsigned long long
#define sort stable_sort
#define INF 0x7f7f7f7f
#define ll long long
using namespace std;
ull p1[1010],p2[1010],h1[1010][1010],h2[1010][1010];
int n,m,ans,a[1010][1010];
int main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>m;
int i,j,l;
ull s1,s2;
char ch;
rep(i,1,n,1) rep(j,1,m,1) cin>>ch,a[i][j] = ch - '0';
p1[0] = p2[0] = 1;
rep(i,1,max(n,m),1) p1[i] = 101 * p1[i - 1],p2[i] = 131 * p2[i - 1];
rep(i,1,n,1) rep(j,1,m,1)
h1[i][j] = h1[i - 1][j] + h1[i][j - 1] - h1[i - 1][j - 1] +
p1[i] * p2[j] * a[i][j];
r(i,n,1,1) r(j,m,1,1)
h2[i][j] = h2[i + 1][j] + h2[i][j + 1] - h2[i + 1][j + 1] +
p1[n - i + 1] * p2[m - j + 1] * a[i][j];
rep(l,1,min(n,m),1) rep(i,1,n - l,1) rep(j,1,m - l,1){
s1 = (h1[i + l][j + l] - h1[i + l][j - 1] - h1[i - 1][j + l] + h1[i - 1][j - 1]) * p1[n - i - l] * p2[m - j - l];
s2 = (h2[i][j] - h2[i + l + 1][j] - h2[i][j + l + 1] + h2[i + l + 1][j + l + 1]) * p1[i - 1] * p2[j - 1];
if(s1 == s2){
ans = l + 1;
i = n,j = m;
}
}
if(ans > 1) cout<<ans;
else cout<<-1;
return 0;
}
统一倍数法
既然我们要减小倍数,保证他们的倍数统一,那么我们不如将倍数扩大保证他们统一,我的方法就是把他扩大成 p 1 n × p 2 m p1^n \times p2^m p1n×p2m,这样他们的倍数就统一了,也可以正常比较了。
/*
*/
#include<bits/stdc++.h>
#define rep(i,s1,s2,s3) for(i = s1;i <= s2;i += s3)
#define r(i,s1,s2,s3) for(i = s1;i >= s2;i -= s3)
#define ull unsigned long long
#define sort stable_sort
#define INF 0x7f7f7f7f
#define ll long long
using namespace std;
ull p1[1010],p2[1010],h1[1010][1010],h2[1010][1010];
int n,m,ans,a[1010][1010];
int main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>n>>m;
int i,j,l;
ull s1,s2;
char ch;
rep(i,1,n,1) rep(j,1,m,1) cin>>ch,a[i][j] = ch - '0';
p1[0] = p2[0] = 1;
rep(i,1,max(n,m),1) p1[i] = 101 * p1[i - 1],p2[i] = 131 * p2[i - 1];//����ģ����unsigned long long��Ȼ����Ϳ�����
rep(i,1,n,1) rep(j,1,m,1)
h1[i][j] = h1[i - 1][j] + h1[i][j - 1] - h1[i - 1][j - 1] +
p1[i] * p2[j] * a[i][j];
r(i,n,1,1) r(j,m,1,1)
h2[i][j] = h2[i + 1][j] + h2[i][j + 1] - h2[i + 1][j + 1] +
p1[n - i + 1] * p2[m - j + 1] * a[i][j];
rep(l,1,min(n,m),1) rep(i,1,n - l,1) rep(j,1,m - l,1){
s1 = (h1[i + l][j + l] - h1[i + l][j - 1] - h1[i - 1][j + l] + h1[i - 1][j - 1]) * p1[n - i] * p2[m - j];
s2 = (h2[i][j] - h2[i + l + 1][j] - h2[i][j + l + 1] + h2[i + l + 1][j + l + 1]) * p1[i + l - 1] * p2[j + l - 1];
if(s1 == s2){
ans = l + 1;
i = n,j = m;
}
}
if(ans > 1) cout<<ans;
else cout<<-1;
return 0;
}