두 원의 중심좌표와 반지름의 길이가 주어졌을 때, 교차범위의 면적을 구하는 문제를 풀어본다.
이 문제의 경우 삼각함수의 성질과 역삼각함수를 알아야 해결 가능하다.
두 원의 위치 관계로 가능한 경우는 다음과 같다.
① 두 원이 서로 접해있거나 떨어져있는 경우 (r1 + r2 <= d)
② 한 원이 다른 원에 들어가있는 경우 (abs(r1 - r2) >= d)
③ 두 개의 교점을 가지는 경우
③의 경우 교차면적의 넓이는 ((r1 ** 2 * (a1 - sin(a1))) - (r2 ** 2 * (a2 - sin(a2)))) / 2 가 된다.
내각 a1 과 a2를 알아내기 위해서는 코사인 제 2 법칙과 역코사인 함수를 사용한다.
※ 제 2 코사인 법칙
위와 같이 삼각형의 세 변이 주어졌을 때, 내각 k에 대해
a^2 + b^2 - 2ab * cos(k) = c^2 가 성립한다.
제 2 코사인 법칙에 따라,
cos(a1) = (r1^2 - r2^2 + d^2) / (2 * r1 * d)
cos(a2) = (r2^2 - r1^2 + d^2) / (2 * r2 * d)
이 된다.
cos(x) = y 일 때, arccos(y) = x 이므로,
a1 = arccos((r1^2 - r2^2 + d^2) / (2 * r1 * d))
a2 = arccos((r2^2 - r1^2 + d^2) / (2 * r2 * d))
이 된다.
교차범위의 면적을 구하기 위해 필요한 두 내각의 값을 얻는 방법만 알면 구현은 간단하다. 이를 구현한 코드는 다음과 같다.
import sys
import math
input = sys.stdin.read
def sol7869():
# 두 원의 중점의 좌표와 반지름 길이
x1, y1, r1, x2, y2, r2 = map(float, input().split())
# 두 원의 중점 사이의 거리
d = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** .5
# 두 원이 외접하거나 떨어져있는 경우
if d >= r1 + r2:
return 0
# 두 원이 내접하거나 포함관계인 경우
if d <= abs(r1 - r2):
return round(math.pi * min(r1, r2) ** 2, 3)
# 두 원이 두 개의 교점을 가지는 경우
# 공식에 따라 두 내각을 구한 뒤 교차면적을 소수점 넷째자리에서 반올림하여 반환
a1, a2 = 2 * math.acos((d ** 2 + (r1 ** 2 - r2 ** 2)) / (2 * r1 * d)), 2 * math.acos((d ** 2 - (r1 ** 2 - r2 ** 2)) / (2 * r2 * d))
return round(1 / 2 * (r1 ** 2 * (a1 - math.sin(a1)) + r2 ** 2 * (a2 - math.sin(a2))), 3)
# 반올림 함수
def round(num, digit):
# 자릿수만큼 10을 곱한 뒤 소수점자리가 5 이상이라면 1을 더해준다
k = 10 ** digit
res = num * k
if res - int(res) >= 0.5:
res += 1
# 정수변환 후 자릿수만큼 10을 나누어 반환
return int(res) / k
이러한 수학공식을 모두 기억하긴 어렵지만 원의 위치관계나 교차면적 등의 문제는 꽤 자주 응용문제로 등장할 수 있기 때문에 기억해두는게 좋을 것 같다.
'코딩테스트 > 알고리즘' 카테고리의 다른 글
#25 동적 계획법(Dynamic Programming) 2 - 심화(1) (0) | 2021.08.31 |
---|---|
#24 비트 마스크(Bit Mask) (0) | 2021.08.30 |
#22 기하 - CCW(Counter-ClockWise) 2 (0) | 2021.08.27 |
#21 기하 - CCW(Counter-ClockWise) 1 (0) | 2021.08.25 |
#20 트리 동적계획법 (0) | 2021.08.23 |