R*Cの単位ブロックからなる長方形の鉄板がある。この鉄板から1辺がkの正方形を切り出す。さらに、4隅のブロックを切り落として、ブレードを作る。この鉄板は厚さが不均一なため、重心とブレードの中点が一致しないことがある。各ブロックの重さが与えられるので、重心とブレードの中点が一致するような最大のブレードのサイズを求める。
方針
正方形の左上のブロックを選ぶ。これを(x, y)とする。
(x+k, y+k)が(r, c)を超えない間、kをインクリメント。
(x, y), (x+k, y+k)のブレードの重心および中点を計算する。一致すれば、答えに反映する。
これで、smallは通る。全体の処理は、O(N^5)。
largeを通すには、計算量を減らす必要がある。(x, y), (x+k, y+k)のブレードの重心計算は、(x, y), (x+k-1, y+k-1)のブレードの重心情報を利用すれば、O(N)で出来る。これは良くやる動的計画。中点は、((x+x+k)/2, (y+y+k)/2)なので、O(1)。これで全体の計算量はO(N^4)に落ちる。結構きわどいが、これでも通る。
Editorial解は、O(N^3)だった。ACrushとかrngさんとかもO(N^3)でやっていた。この方法は、非常に興味深い。
処理の概要は以下のとおり。
- (0, 0), (x, y), x in {0,1,..,R-1}, y in {0,1,...C-1}のブレードの重心を予め計算しておく。このとき、(0,0),(x, y)の重心は、(0,0),(x-1,y-1)と、(0,0), (x-1,y)と、 (0,0),(x, y-1)の情報を利用すれば、O(1)で出来る。よって、1.の処理はO(N^2)
- (x, y)(x+k, y+k)のブレードの重心は、(x, y) = (0, 0)の場合は、1.をそのまま使える。そうでない場合は、(0,0), (x+k, y+k)と、(0,0),(x+k, y-1)と、(0, 0),(x-1,y+k)と、(0,0),(x-1,y-1)の情報を使ってO(1)で求めることができる。よって2.の処理は、(x,y,k)のループのみとなり、O(N^3)
コードに落とすときの工夫だが、2.の場合分け(x=0とかy=0とか)をごちゃごちゃしなくていいように、上行と左列にdummyのバッファを入れておくとよい。
以下ソース。
int mass[512][512];
LL rec_m[512][512];
LL rec_gx[512][512];
LL rec_gy[512][512];
void gcjMain() {
int t;
cin >> t;
REP(ii, t) {
int r, c, d;
cin >> r >> c >> d;
vector<string> mtx(r);
REP(i, r)
cin >> mtx[i];
REP(i, r) REP(j, c)
mass[i][j] = mtx[i][j] - '0';
memset(rec_m, 0, sizeof(rec_m));
memset(rec_m, 0, sizeof(rec_gx));
memset(rec_m, 0, sizeof(rec_gy));
REP(i, r) REP(j, c) {
rec_m[i+1][j+1] = rec_m[i][j+1] + rec_m[i+1][j] - rec_m[i][j] + mass[i][j];
rec_gx[i+1][j+1] = rec_gx[i][j+1] + rec_gx[i+1][j] - rec_gx[i][j] + i * mass[i][j];
rec_gy[i+1][j+1] = rec_gy[i][j+1] + rec_gy[i+1][j] - rec_gy[i][j] + j * mass[i][j];
}
int ret = -1;
REP(i, r) REP(j, c) {
for (int k = 2; i + k < r && j + k < c; k++) {
LL ms;
LL gx;
LL gy;
ms = rec_m[i+1+k][j+1+k] + rec_m[i][j] - rec_m[i+1+k][j] - rec_m[i][j+1+k];
gx = rec_gx[i+1+k][j+1+k] + rec_gx[i][j] - rec_gx[i+1+k][j] - rec_gx[i][j+1+k];
gy = rec_gy[i+1+k][j+1+k] + rec_gy[i][j] - rec_gy[i+1+k][j] - rec_gy[i][j+1+k];
ms -= mass[i][j] + mass[i][j+k] + mass[i+k][j] + mass[i+k][j+k];
gx -= i*mass[i][j] + i*mass[i][j+k] + (i+k)*mass[i+k][j] + (i+k)*mass[i+k][j+k];
gy -= j*mass[i][j] + (j+k)*mass[i][j+k] + j*mass[i+k][j] + (j+k)*mass[i+k][j+k];
if (ms * (i + i + k) == 2 * gx && ms * (j + j + k) == 2 * gy)
ret = max(ret, k+1);
}
}
if (ret == -1)
printf("Case #%d: IMPOSSIBLE\n", ii+1);
else
printf("Case #%d: %d\n", ii+1, ret);
}
}
0 件のコメント:
コメントを投稿