알고스팟 문제 링크: https://algospot.com/judge/problem/read/FOSSIL
algospot.com :: FOSSIL
꽃가루 화석 문제 정보 문제 봄마다 비염 환자들을 괴롭히는 꽃가루는 종종 과거의 기후 변화를 추적하는 데 유용하게 사용됩니다. 퇴적층에서 발견되는 꽃가루 화석을 통해 각 지방의 기후가
algospot.com
볼록 다각형을 이루는 점들의 좌표가 반시계 방향으로 각각 두개 주어질때, 두 볼록 다각형이 이루는 교집합 다각형의 세로 길이 최댓값을 구하는 문제이다.
두 볼록 다각형의 교집합 다각형은 볼록 다각형이고 이는 삼분법을 이용하여 x좌표를 탐색하면 정답을 찾을 수 있다는것을 이미한다.
교집합 다각형을 추상화 하는 방법은 일단 각각의 볼록 다각형들의 오목 부분과 볼록부분을 나눠서 두 볼록 다각형 구분없이 저장한다.
그 다음 x좌표에 따른 교집합 다각형의 세로 길이를 구할때마다 x좌표에 해당하는 위로 볼록 부분중 작은 쪽, 오목 부분중 큰 쪽의 y좌표 차이를 확인하면 된다.
이제 x좌표에 따른 교집합 다각형의 세로 길이를 구할 수 있고 삼분법을 이용하면 적절한 답을 추적할 수 있다.
#include<iostream> | |
#include<vector> | |
using namespace std; | |
struct point { | |
double x, y; | |
}; | |
// 두 다각형을 좌표로 표현 | |
vector<point> hull1, hull2; | |
// 다각형의 위로 볼록부분을 upper에 오목부분을 lower에 다각형 구분없이 넣는다 | |
vector<pair<point, point>> upper, lower; | |
void decompose(const vector<point>& hull) { | |
int n = hull.size(); | |
for (int i = 0; i < n; i++) { | |
if (hull[i].x < hull[(i + 1) % n].x) | |
lower.push_back(make_pair(hull[i], hull[(i + 1) % n])); | |
else if (hull[i].x > hull[(i + 1) % n].x) | |
upper.push_back(make_pair(hull[i], hull[(i + 1) % n])); | |
} | |
} | |
// x가 a.x와 b.x 사이에 있는가 | |
bool between(const point& a, const point& b, double x) { | |
return((a.x <= x && b.x >= x) || (a.x >= x && b.x <= x)); | |
} | |
// 두 점 a,b를 지나는 직선 위에 있는 점의 x좌표가 x일때, return y좌표 | |
double at(const point& a, const point& b, double x) { | |
double dy = b.y - a.y, dx = b.x - a.x; | |
return dy / dx * (x - a.x) + a.y; | |
} | |
// x에서 교집합 다각형의 세로길이 | |
double vertical(double x) { | |
double minUp = 1e20, maxLow = -1e20; | |
for (int i = 0; i < upper.size(); i++) | |
// x를 포함하는 두 다각형의 위로 오목부분 중에서 | |
if (between(upper[i].first, upper[i].second, x)) | |
// 더 작은것이 교집합 다각형의 윗 부분이다 | |
minUp = min(minUp, at(upper[i].first, upper[i].second, x)); | |
for (int i = 0; i < lower.size(); i++) | |
// x를 포함하는 두 다각형의 위로 볼록부분 중에서 | |
if (between(lower[i].first, lower[i].second, x)) | |
// 더 큰것이 교집합 다각형의 아랫 부분이다 | |
maxLow = max(maxLow, at(lower[i].first, lower[i].second, x)); | |
return minUp - maxLow; | |
} | |
// 다각형 hull에서 제일 작은 x좌표 | |
double minX(const vector<point>& hull) { | |
double ret = 1e20; | |
for (int i = 0; i < hull.size(); i++) | |
ret = min(ret, hull[i].x); | |
return ret; | |
} | |
// 다각형 hull에서 제일 큰 x좌표 | |
double maxX(const vector<point>& hull) { | |
double ret = -1e20; | |
for (int i = 0; i < hull.size(); i++) | |
ret = max(ret, hull[i].x); | |
return ret; | |
} | |
double solve() { | |
// 각각의 다각형에 제일 작은 x좌표중 큰쪽이 lo의 초기값 | |
double lo = max(minX(hull1), minX(hull2)); | |
// 각각의 다각형에 제일 큰 x좌쵸중 작은쪽이 hi의 초기값 | |
double hi = min(maxX(hull1), maxX(hull2)); | |
if (hi < lo)return 0; | |
// 삼분법 실행 | |
for (int i = 0; i < 100; i++) { | |
double aab = (lo * 2 + hi) / 3.0; | |
double abb = (lo + hi * 2) / 3.0; | |
if (vertical(aab) < vertical(abb)) | |
lo = aab; | |
else | |
hi = abb; | |
} | |
return max(0.0, vertical(hi)); | |
} | |
int main() { | |
cout << fixed; | |
cout.precision(8); | |
int testcase; | |
cin >> testcase; | |
while (testcase--) { | |
hull1.clear(); hull2.clear(); | |
upper.clear(); lower.clear(); | |
int n, m; | |
cin >> n >> m; | |
for (int i = 0; i < n; i++) { | |
point dot; | |
cin >> dot.x >> dot.y; | |
hull1.push_back(dot); | |
} | |
for (int i = 0; i < m; i++) { | |
point dot; | |
cin >> dot.x >> dot.y; | |
hull2.push_back(dot); | |
} | |
decompose(hull1); decompose(hull2); | |
cout << solve() << endl; | |
} | |
} |
코드 원본: https://github.com/sbl133/JongmanBook/blob/main/13.%20NumericalAnalysis/RATIO.cpp
GitHub - sbl133/JongmanBook
Contribute to sbl133/JongmanBook development by creating an account on GitHub.
github.com

댓글을 통한 코드리뷰, 질문, 지적 언제든 환영입니다!
reference: 프로그래밍 대회에서 배우는 알고리즘 문제해결전략1
'Algorithm > algospot' 카테고리의 다른 글
algospot 마법의 약 (문제 ID: POTION) c++ (0) | 2021.09.12 |
---|---|
algospot 비밀번호 486 (문제 ID: PASS486) c++ (0) | 2021.09.12 |
algospot 승률 올리기 (문제 ID: RATIO) c++ (0) | 2021.09.09 |
algospot 수강 철회 (문제 ID: WITHDRAWAL) C++ (0) | 2021.09.07 |
algospot 캐나다 여행 (문제 ID: CANADATRIP) C++ (0) | 2021.09.07 |