ABC 130F : Minimum Bounding Box
問題文:https://atcoder.jp/contests/abc130/tasks/abc130_f
概要
日本語なので略。
解法
を 動く方向が L の点の座標の最小値とする。他の方向についても同様とする。
すると、時刻 での 座標の最小値 は、
と書ける。
他の座標も同様の形で書き下すことができる。上記より、時刻 での題意の値
は、(各時刻におけるの値は時間で計算できるので、)すべてのについてこれらの値を求めればその最小値を求めることで解を得られる。しかしの候補は無限にあるため、候補となるを絞り効率的に答えを求める必要がある。
は、非負整数およびを用いて
と書けることに注意する。
2つの項のの係数は常にからのいずれかになる。これらの符号が共に負のときははできるだけ大きいほうがよい。ともに正のときはできるだけ小さいほうが良い.符号が違うときは上に凸な2次関数となるため、極大値をとる点から離れるほど関数値が小さくなる。したがって、値は、関数形が一意に定まる区間ではその端点で最小値をとる。
よって、などの関数形が変化する点のみで値を計算すれば良い。このような点は定数個しかないため、 などをもとめるところがボトルネックになる。時間計算量はとなる。
コード
#include <bits/stdc++.h> using namespace std; typedef long long Int; const Int INF = 2000000000ll; Int x_min[128], y_min[128], x_max[128], y_max[128]; double f(double t) { if (t < 0) return 1e18; double x_ma = max({(double)max(x_max['U'], x_max['D']), x_max['R'] + t, x_max['L'] - t}); double x_mi = min({(double)min(x_min['U'], x_min['D']), x_min['R'] + t, x_min['L'] - t}); double y_ma = max({(double)max(y_max['L'], y_max['R']), y_max['U'] + t, y_max['D'] - t}); double y_mi = min({(double)min(y_min['L'], y_min['R']), y_min['U'] + t, y_min['D'] - t}); return (x_ma - x_mi) * (y_ma - y_mi); } int main() { int N; scanf("%d", &N); for (int i = 0; i < 128; i++) { x_min[i] = y_min[i] = INF; x_max[i] = y_max[i] = -INF; } for (int i = 0; i < N; i++) { Int x, y; char s[2]; scanf("%lld %lld %s", &x, &y, s); char c = s[0]; x_min[c] = min(x_min[c], x); x_max[c] = max(x_max[c], x); y_min[c] = min(y_min[c], y); y_max[c] = max(y_max[c], y); } vector<double> cand; cand.push_back(0); cand.push_back(x_max['L'] - max(x_max['U'], x_max['D'])); cand.push_back((x_max['L'] - x_max['R']) * 0.5); cand.push_back(max(x_max['U'], x_max['D']) - x_max['R']); cand.push_back(x_min['L'] - min(x_min['U'], x_min['D'])); cand.push_back((x_min['L'] - x_min['R']) * 0.5); cand.push_back(min(x_min['U'], x_min['D']) - x_min['R']); cand.push_back(y_max['D'] - max(y_max['L'], y_max['R'])); cand.push_back((y_max['D'] - y_max['U']) * 0.5); cand.push_back(max(y_max['L'], y_max['R']) - y_max['U']); cand.push_back(y_min['D'] - min(y_min['L'], y_min['R'])); cand.push_back((y_min['D'] - y_min['U']) * 0.5); cand.push_back(min(y_min['L'], y_min['R']) - y_min['U']); double ans = 1e18; for (int i = 0; i < cand.size(); i++) { ans = min(ans, f(cand[i])); } printf("%.10lf\n", ans); return 0; }
コメント
実装が偉いことになったんですが、どうやったら綺麗に実装できるんでしょうか……。
それと特にジャンルも決めずに問題を解いているので、何かオススメの問題がありましたらこの記事のコメントや Twitter のリプライで連絡していただけると嬉しいです。