P4330 [COCI 2006/2007 #1] Debug / P2177 内存杀手
题目描述
中文题面见洛谷P2177。
While debugging a program Mirko noticed that a bug in the program may be linked with the existence of so called square killers in the program memory. The program memory is a matrix composed of R \texttt R R rows and C \texttt C C columns consisting only of zeroes and ones. A square killer is a square submatrix in memory, consisting of more than one character, that, when rotated 180 180 180 degrees looks exactly the same. For example, the following matrix contains 3 3 3 square killers:
Mirko is wondering if there is a connection between the size of the largest square killer and the bug in the program. Help Mirko by writing a program that, given the layout of the memory, outputs the size of the largest square killer. The size of the square killer is the number of rows (or columns) that the killer consists of. In the example above the killer sizes are
2
2
2,
2
2
2 and
3
3
3, respectively.
输入格式
The first will contain two integers, R \texttt R R and C \texttt C C, smaller than or equal to 300 300 300. The next R \texttt R R lines will each contain C \texttt C C characters (‘ 0 0 0’ or ‘ 1 1 1’) with no spaces.
输出格式
Output the size of the largest killer on a single line, or output - 1 \texttt -1 -1 if there are no square killers.
样例
样例输入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
数据范围
1 ≤ R,C ≤ 300 1 \le \texttt {R,C} \le 300 1≤R,C≤300
题解
题目让我们在一个 R × C \texttt{R} \times \texttt{C} R×C 的矩形中,求一个边长为 k k k 的正方形, k > 1 k > 1 k>1,其本身与旋转 180 ˚ 180\r{} 180˚ 的正方形一样,求最大的 k k k,没有则输出 − 1 -1 −1。
由于要求最大的边长,所以从 min ( R,C ) \min(\texttt{R,C}) min(R,C) 开始枚举边长,一直枚举到 1 1 1,然后在枚举正方形的任意一个端点进行判断。
运用二维哈希,将矩阵本身和旋转 180 ˚ 180\r{} 180˚ 后的矩阵的二维哈希值求出来。对于一个边长为 k k k,右下端点为 i , j i, j i,j,在原矩形中四个端点为 ( i − k + 1 , j − k + 1 ) , ( i − k + 1 , j ) , ( i , j − k + 1 ) , ( i , j ) (i - k + 1, j - k + 1),(i - k + 1,j),(i,j - k + 1),(i,j) (i−k+1,j−k+1),(i−k+1,j),(i,j−k+1),(i,j),对应在旋转 180 ˚ 180\r{} 180˚ 后的矩阵上为 ( R − i + 1 , C − j + 1 ) , ( R − i + 1 , C − j + k ) , ( R − i + k , C − j + 1 ) , ( R − i + k , C − j + k ) (R - i + 1,C - j + 1),(R - i + 1, C - j + k),(R - i + k, C - j + 1),(R - i + k, C - j + k) (R−i+1,C−j+1),(R−i+1,C−j+k),(R−i+k,C−j+1),(R−i+k,C−j+k)。
哈希预处理时间复杂度为 O ( R ⋅ C ) O(\texttt{R} \cdot \texttt{C}) O(R⋅C),枚举复杂度为 O ( min ( R,C ) ⋅ R ⋅ C ) O(\min(\texttt{R,C}) \cdot \texttt{R} \cdot \texttt{C}) O(min(R,C)⋅R⋅C),哈希判断为 O ( 1 ) O(1) O(1),总复杂度为 O ( n 3 ) O(n^3) O(n3),可以通过此题。
如果不会二维哈希,可以去查阅资料,这里简单说一下。
参考一维哈希,前缀和为 h i = h i − 1 × b a s e + a i h_{i} = h_{i - 1} \times base + a_i hi=hi−1×base+ai,差分为 S h ( l , r ) = h r − h l − 1 × b a s e r − l + 1 S_h(l, r) = h_r - h_{l - 1} \times base^{r - l + 1} Sh(l,r)=hr−hl−1×baser−l+1。
二维哈希同理,可以参考二维前缀和, h i , j = h i − 1 , j + h i , j − 1 − h i − 1 , j − 1 + a i , j h_{i,j} = h_{i - 1, j} + h_{i, j - 1} - h_{i - 1, j - 1} + a_{i, j} hi,j=hi−1,j+hi,j−1−hi−1,j−1+ai,j,差分为 S h ( l 1 , r 1 , l 2 , r 2 ) = ( h l 2 , r 2 − h l 1 − 1 , r 2 − h l 2 , r 1 − 1 + h l 1 − 1 , r 1 − 1 ) ⋅ ( b a s e 1 l 1 − 1 ) − 1 ⋅ ( b a s e 2 r 1 − 1 ) − 1 S_h(l_1, r_1, l_2, r_2) = (h_{l2, r2} - h_{l1 - 1, r2} - h_{l2, r1 - 1} + h_{l1 - 1, r1 - 1}) \cdot (base1^{l1 - 1})^{-1} \cdot (base2^{r1-1})^{-1} Sh(l1,r1,l2,r2)=(hl2,r2−hl1−1,r2−hl2,r1−1+hl1−1,r1−1)⋅(base1l1−1)−1⋅(base2r1−1)−1,乘上的逆元是将矩阵对齐到左上角。但这样的时间复杂度较高,要求逆元,考虑优化。
以各维度到右下角的距离作为 b a s e base base 的指数,从而避免逆元运算。
前缀和:
h
i
,
j
=
h
i
−
1
,
j
⋅
b
a
s
e
1
+
h
i
,
j
−
1
⋅
b
a
s
e
2
−
h
i
−
1
,
j
−
1
⋅
b
a
s
e
1
⋅
b
a
s
e
2
+
a
i
,
j
h_{i, j} = h_{i - 1, j} \cdot base1 + h_{i, j - 1} \cdot base2 - h_{i - 1, j - 1} \cdot base1 \cdot base2 + a_{i, j}
hi,j=hi−1,j⋅base1+hi,j−1⋅base2−hi−1,j−1⋅base1⋅base2+ai,j
差分:
S
h
(
l
1
,
r
1
,
l
2
,
r
2
)
=
h
(
l
2
,
r
2
)
−
h
l
2
,
r
1
−
1
⋅
b
a
s
e
2
r
2
−
r
1
+
1
−
h
l
1
−
1
,
r
2
⋅
b
a
s
e
1
l
2
−
l
1
+
1
+
h
l
1
−
1
,
r
1
−
1
⋅
b
a
s
e
1
l
2
−
l
1
+
1
⋅
b
a
s
e
2
r
2
−
r
1
+
1
S_h(l_1, r_1, l_2, r_2) = h(l_2, r_2) - h_{l2, r1 - 1} \cdot base2^{r2 - r1 + 1} - h_{l1 - 1, r2} \cdot base1^{l2 - l1 + 1} + h_{l1 - 1, r1 - 1} \cdot base1^{l2 - l1 + 1} \cdot base2^{r2 - r1 + 1}
Sh(l1,r1,l2,r2)=h(l2,r2)−hl2,r1−1⋅base2r2−r1+1−hl1−1,r2⋅base1l2−l1+1+hl1−1,r1−1⋅base1l2−l1+1⋅base2r2−r1+1
具体细节见代码。
const unsigned long long base1 = 1009, base2 = 131;
int n, m;//R,C
int a[2][310][310];//a[0] 为原始矩阵,a[1] 为旋转后的矩阵
unsigned long long fac[2][90010];//fac[0][i] 表示 base1^i,fac[1][i] 表示 base2^i,用于逆元
unsigned long long h[2][310][310];//hash矩阵的值
unsigned long long get_sum(int l1, int r1, int l2, int r2, int i){//计算左上角为 (l1,r1),右下角为 (l2,r2) 的矩阵哈希值
return h[i][l2][r2] - h[i][l2][r1 - 1] * fac[1][r2 - r1 + 1] - h[i][l1 - 1][r2] * fac[0][l2 - l1 + 1] + h[i][l1 - 1][r1 - 1] * fac[0][l2 - l1 + 1] * fac[1][r2 - r1 + 1];
}
int main(){
//预处理逆元
fac[0][0] = fac[1][0] = 1;
for(int i = 1; i <= 90000; ++ i){
fac[0][i] = fac[0][i - 1] * base1;
fac[1][i] = fac[1][i - 1] * base2;
}
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m; ++ j){
char x;
cin >> x;
a[0][i][j] = x - '0';
}
}
//旋转矩阵
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m; ++ j){
a[1][i][j] = a[0][n - i + 1][m - j + 1];
}
}
//求 hash 值
for(int k = 0; k < 2; ++ k){
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m; ++ j){
h[k][i][j] = h[k][i][j - 1] * base2 + h[k][i - 1][j] * base1 - h[k][i - 1][j - 1] * base1 * base2 + a[k][i][j];
}
}
}
int t = min(n, m);
//枚举边长和右下端点
for(int k = t; k > 1; -- k){
for(int i = k; i <= n; ++ i){
for(int j = k; j <= m; ++ j){
if(get_sum(i - k + 1, j - k + 1, i, j, 0) == get_sum(n - i + 1, m - j + 1, n - i + k, m - j + k, 1)){
printf("%d", k);
return 0;
}
}
}
}
printf("-1");
return 0;
}