平行移動量の候補を絞る考え方を使う!
問題概要
座標平面上に 個の星を表す点 が与えられる(星座を成している)。また、それとは別に座標平面上に 個の点 が与えられる。
点 を一斉にある量だけ平行移動すると、それらすべてが、上に述べた 個の点のどれかに重なるという。また、そのような移動量は一意に定まることが保証されるという。
その移動量を求めよ。
制約
- 個の点の 座標と 座標はすべて 以上 以下の整数
考えたこと
まずパッと思いつく方法は、 個の点が存在し得るような座標値の範囲内で、平行移動量をすべて調べるという方法だろう。だが、平行移動量として考えられるのは
- 軸方向: 以上 以下
- 軸方向: 以上 以下
ということで、全部で 通りありうる。これは到底調べきれない。
そこで、「もし適切に平行移動したならば、星 は 個の点のいずれかに重なっている」ことに着目しよう。つまり、平行移動量は以下の 通りのみを調べれば十分なのだ。
- 星 が星 に重なるように平行移動する場合
- 星 が星 に重なるように平行移動する場合
- ...
- 星 が星 に重なるように平行移動する場合
通りを調べるだけならば、十分に現実的だと考えられる。次に、平行移動量 を決めたときに、その平行移動量 が適するかどうかを判定する方法を考える。
平行移動量 が適するかの判定
個の星 に対して、 だけ平行移動して得られる座標が、 個の星 のいずれかに一致するかを調べれば良い。
ここで、 個の星の座標をあらかじめ集合型などに格納しておくとよい(C++ であれば set
型)。C++ の set
型を用いた場合、平行移動して得られる座標が、 個の星の座標のいずれかに一致するかを の計算量で判定できる。
よって、 個の星全てについて判定するのに要する計算量は となる。
まとめ
まとめると、次の解法で解ける。
- 星 が星 に重なるように平行移動する 通りの場合を調べていく( 通り)
- 各平行移動量について、星 を平行移動して得られる座標が、星 のいずれかに一致するかを調べる(計算量 )
全体の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; using pint = pair<int, int>; int main() { int M, N; cin >> M; vector<int> sx(M), sy(M); for (int i = 0; i < M; i++) cin >> sx[i] >> sy[i]; cin >> N; vector<int> tx(N), ty(N); for (int i = 0; i < N; i++) cin >> tx[i] >> ty[i]; // N 個の星の位置情報を set に入れておく set<pint> stars; for (int i = 0; i < N; i++) stars.insert(pint(tx[i], ty[i])); // 星座の 0 番目の星が、写真の i 番目の星と重なるかを check する int resx, resy; for (int i = 0; i < N; i++) { // dx, dy: 平行移動量 int dx = tx[i] - sx[0], dy = ty[i] - sy[0]; // 星座の j 番目の星を dx, dy だけ移動した位置に、星があるかを check する bool ok = true; for (int j = 0; j < M; j++) { if (!stars.count(pint(sx[j] + dx, sy[j] + dy))) ok = false; } if (ok) resx = dx, resy = dy; } cout << resx << " " << resy << endl; }