https://acm.sjtu.edu.cn/OnlineJudge/problem/1055
先明确一下重心的公式。在这道题中,从
(x,y)
(
x
,
y
)
到
(p,q)
(
p
,
q
)
的一块长方形巧克力的重心
(xG,yG)
(
x
G
,
y
G
)
位于
xG=∑pi=x(i∑qj=yρ(i,j))∑pi=x∑qj=yρ(i,j), yG=∑qj=y(j∑pi=xρ(i,j))∑pi=x∑qj=yρ(i,j).
x
G
=
∑
i
=
x
p
(
i
∑
j
=
y
q
ρ
(
i
,
j
)
)
∑
i
=
x
p
∑
j
=
y
q
ρ
(
i
,
j
)
,
y
G
=
∑
j
=
y
q
(
j
∑
i
=
x
p
ρ
(
i
,
j
)
)
∑
i
=
x
p
∑
j
=
y
q
ρ
(
i
,
j
)
.
简单来说,就是坐标用质量(这里用密度代替)加权平均。
看到这题,我的第一思路是做质量的前缀和,然后枚举大小和左上角进行计算。但这样做的极限时间大概是 300×2∑3k=300k(301−k)2=4×1011 300 × 2 ∑ k = 300 3 k ( 301 − k ) 2 = 4 × 10 11 ,必定会超时。于是经过了对重心计算式的仔细观察,发现分子上的加权平均也可以通过前缀和的方式直接计算,这样就把验证某个方案是否可行的时间复杂度从 O(k3) O ( k 3 ) 降到了 O(1) O ( 1 ) ,仅剩的枚举复杂度是 O(cr×min(c,r)) O ( c r × min ( c , r ) ) 。虽然题目里没有说一共有多少组数据,但应该是不会超时的。
最后,通过前缀和计算重心位置时,注意把正方形的四个角去掉,还要注意质量和为零的情况。
核心部分如下:
int a[305][305], sum[305][305];
int sumx[305][305], sumy[305][305];
//sum表示简单的质量和,sumx表示x方向的加权平均,sumy表示y方向的加权平均
//=======================================
for (int i = 1; i <= r; ++i){
for (int j = 1; j <= c; ++j){
cin >> ch;
a[i][j] = ch - '0';
sum[i][j] = a[i][j] + sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1];
}
}
for (int i = 1; i <= r; ++i){
for (int j = 1; j <= c; ++j){
sumx[i][j] = sumx[i - 1][j] + i * (sum[i][j] - sum[i - 1][j]);
sumy[i][j] = sumy[i][j - 1] + j * (sum[i][j] - sum[i][j - 1]);
}
}
//==========================================
for (int k = min(r, c); k >= 3; --k){
for (int i = 1; i <= r - k + 1; ++i){
for (int j = 1; j <= c - k + 1; ++j){
int x = i + k - 1, y = j + k - 1;
int s = sum[x][y] - sum[i - 1][y] - sum[x][j - 1] + sum[i - 1][j - 1]
- a[i][j] - a[i][y] - a[x][j] - a[x][y];
double wx = sumx[x][y] - sumx[i - 1][y] - sumx[x][j - 1] + sumx[i - 1][j - 1]
- i * a[i][j] - i * a[i][y] - x * a[x][j] - x * a[x][y];
double wy = sumy[x][y] - sumy[i - 1][y] - sumy[x][j - 1] + sumy[i - 1][j - 1]
- j * a[i][j] - y * a[i][y] - j * a[x][j] - y * a[x][y];
if (s == 0){.../*成立*/}
wx = wx / s;
wy = wy / s;
if (abs(2 * wx - i - x) < 1e-8 && abs(2 * wy - j - y) < 1e-8){.../*成立*/}
}
}
}