AOJ 1394: Fair Chocolate-Cutting
この記事は帰ってきた AOJ-ICPC Advent Calendar 2022 1 日目の記事です。
https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1394
問題概要
頂点の凸多角形が与えられる。この多角形の辺上に端点があり、多角形の面積を 等分する線分の長さとしてありうる最大値と最小値を求めよ。
解法
多角形の頂点を反時計回りに 、面積を とします。また、 であるとします。
線分の端点を (ただし は辺 上、 は辺 上の点) とします。多角形 の面積を 、多角形 の面積を とおくと、明らかに が必要です。
辺 と辺 が平行かどうかで場合分けをします。
辺 と辺 が平行でない場合
直線 と直線 の交点を とします。このとき、 が半直線 上にある場合のみ考えるとしてよいです。(そうでない場合、 と を逆にして考える)
とおくと、 の取りうる値の範囲は となります。また、多角形 の面積は となります。これが と等しくなればよいので、式変形して
となります。右辺を とおくと、 です。よって、 とおくと、 の取りうる値の範囲は となります。
線分 の長さは余弦定理より です。よって の最大値と最小値を求めればよいですが、これは に等しいので、 の最大値と最小値を考えればよいです。
において、 は下に凸な関数で で極小値を取ります。従って、最大値を与える の候補は 、最小値を与える の候補は となります。
辺 と辺 が平行な場合
とおくと、と、 の取りうる値の範囲は となります。また、直線 の距離を とおくと、多角形 の面積は となります。これが と等しくなればよいので、式変形して
となります。右辺を とおくと、 です。よって、 とおくと、 の取りうる値の範囲は となります。
幾何的な考察から、最大値を与える の候補は 、最小値の候補は と となるような です。 となるような が存在するかは、内積の計算で判定することができます。
実装
#include <bits/stdc++.h> using namespace std; const long double eps = 0.000001; const long double INF = 100000; struct point{ long double x, y; point(){ } point(long double x, long double y): x(x), y(y){ } point operator +(point P){ return point(x + P.x, y + P.y); } point operator -(point P){ return point(x - P.x, y - P.y); } point operator *(long double k){ return point(x * k, y * k); } point operator /(long double k){ return point(x / k, y / k); } }; long double abs(point P){ return sqrt(P.x * P.x + P.y * P.y); } long double dist(point P, point Q){ return abs(Q - P); } point unit(point P){ return P / abs(P); } long double dot(point P, point Q){ return P.x * Q.x + P.y * Q.y; } long double cross(point P, point Q){ return P.x * Q.y - P.y * Q.x; } long double angle_cosine(point P, point Q){ return dot(P, Q) / (abs(P) * abs(Q)); } long double angle_sine(point P, point Q){ return abs(cross(P, Q)) / (abs(P) * abs(Q)); } long double angle_one_minus_cosine(point P, point Q){ return 1 - angle_cosine(P, Q); } long double area(point A, point B, point C){ return abs(cross(B - A, C - A)) / 2; } struct line{ point A, B; line(){ } line(point A, point B): A(A), B(B){ } }; point vec(line L){ return L.B - L.A; } double point_line_distance(point P, line L){ return abs(cross(P - L.A, vec(L))) / abs(vec(L)); } point line_intersection(line L1, line L2){ return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2)); } long double get_dist(long double a, long double b, long double one_minus_cost){ return sqrt(max(pow(a - b, 2) + 2 * a * b * one_minus_cost, (long double) 0)); } int main(){ cout << fixed << setprecision(20); int n; cin >> n; vector<point> P(n + 1); for (int i = 0; i < n; i++){ cin >> P[i].x >> P[i].y; } P[n] = P[0]; long double S = 0; for (int i = 0; i < n; i++){ S += cross(P[i], P[i + 1]) / 2; } long double mn = INF, mx = 0; for (int i = 0; i < n; i++){ long double S1 = 0, S2 = S; for (int p = 1; p < n; p++){ int j = (i + p) % n; S2 -= area(P[i], P[j], P[j + 1]); if (p >= 3){ int j2 = (j - 1 + n) % n; S1 += area(P[j], P[j2], P[i + 1]); } if (S1 <= S / 2 + eps && S2 <= S / 2 + eps){ long double C = cross(P[j + 1] - P[j], P[i] - P[i + 1]); if (C > eps){ long double sint = angle_sine(P[j + 1] - P[j], P[i] - P[i + 1]); long double one_minus_cost = angle_one_minus_cosine(P[j + 1] - P[j], P[i] - P[i + 1]); point A = line_intersection(line(P[i], P[i + 1]), line(P[j], P[j + 1])); long double a1 = dist(A, P[i + 1]); long double a2 = dist(A, P[i]); long double b1 = dist(A, P[j]); long double b2 = dist(A, P[j + 1]); long double pr = a1 * b1 + (S - S1 * 2) / sint; long double xmn = max(a1, pr / max(b2, eps)); long double xmx = min(a2, pr / max(b1, eps)); if (xmn <= xmx + eps){ long double d1 = get_dist(xmn, pr / xmn, one_minus_cost); mn = min(mn, d1); mx = max(mx, d1); long double d2 = get_dist(xmx, pr / xmx, one_minus_cost); mn = min(mn, d2); mx = max(mx, d2); long double x = sqrt(pr); if (xmn - eps <= x && x <= xmx + eps){ long double d3 = get_dist(x, x, one_minus_cost); mn = min(mn, d3); } } } else if (C > -eps){ long double a = dist(P[i], P[i + 1]); long double b = dist(P[j], P[j + 1]); long double h = point_line_distance(P[i + 1], line(P[j], P[j + 1])); long double s = (S - S1 * 2) / h; if (-eps <= s && s <= a + b + eps){ long double xmn = max((long double) 0, s - b); long double xmx = min(a, s); point P1 = P[i + 1] + unit(P[i] - P[i + 1]) * xmn; point Q1 = P[j] + unit(P[j + 1] - P[j]) * (s - xmn); long double d1 = dist(P1, Q1); mn = min(mn, d1); mx = max(mx, d1); point P2 = P[i + 1] + unit(P[i] - P[i + 1]) * xmx; point Q2 = P[j] + unit(P[j + 1] - P[j]) * (s - xmx); long double d2 = dist(P2, Q2); mn = min(mn, d2); mx = max(mx, d2); if (dot(P[i] - P1, Q1 - P1) > -eps && dot(P[i] - P2, Q2 - P2) < eps){ mn = min(mn, h); } } } } } } cout << mn << endl; cout << mx << endl; }