블로그

BOJ 공약수 - 2436

게시 3분

2436번: 공약수

초등학생 수준의 수학으로 해결할 수 있는 문제다.(유클리드 호제법 제외)
최소공배수를 L, 최대공약수를 G라고 하자.

초등학생 때 저런식으로 G와 L을 구했던 기억이 있을것이다.
A와 B를 나눌 수 있을 때까지 계속 나누고 나눈 수들의 곱을 G라고 하고
G x a x b = L 로 구할 수 있었다.
G로 나누면, L / G = a x b 이 된다.
또한, A와 B를 나눌 수 없을때까지 나누고 남은 수가 a와 b이므로
a와 b는 서로소다.

이제 문제로 넘어가서 입력값에 L와 G가 주어지므로 L/G를 1부터 나눠보며 a, b를 구한다.
그리고 수의 차이가 최소며 서로소인 두 수를 구하면 된다.
또는 두 수의 최대공약수가 1이어도 서로소이다.

마지막으로 a, b에 G를 곱하면 A, B가 된다.

시간안에 해결될까? 시간복잡도를 계산해보자.

먼저 어떤 수 N을 1부터 나눈다고 해보자.
Trial Division 소수 판정법처럼 sqrt(N)까지만 나눠보면 된다.
때문에 a를 1부터 sqrt(L/G)까지만 나눠서 a와 b를 찾으면 시간이 줄어든다.

a와 b의 서로소 계산을 위해서는 2부터 sqrt(min(a, b)) 까지 나눠서
a와 b가 모두 나눠지는 경우가 있는지 확인한다. ( O(sqrt(min(a, b)) 의 시간복잡도를 가진다.)
or
gcd 알고리즘을 통해 최대공약수가 1인지 체크한다. ( O(log(a+b))의 시간복잡도를 가진다. )

첫번째 방법의 시간복잡도는 O(sqrt(N) * sqrt(min(a, b)) 이고,
두번째 방법의 시간복잡도는 O(sqrt(N) * log(a+b)) 이다.

문제에서 N이 가장 커질 때는 G=2, L=1억 일때다.
그럼 G/L = 5천만 이 되고, min(a, b)는 대충 최대값 잡아서
a=sqrt(5천만), b=sqrt(5천만) 으로 하자.
때문에 다음을 계산하면 된다.

첫번째 방법 계산

50000000⋅50000000 = 594603.5575\sqrt{50000000}\cdot \sqrt{\sqrt{50000000}}\ =\ 594603.5575

두번째 방법 계산

50000000⋅log⁡2(250000000) = 97493.8492...\sqrt{50000000}\cdot \log _2\left(2\sqrt{50000000}\right)\ =\ 97493.8492...

대충 60만, 10만므로 시간안에 '충분히' 해결된다.
또한 1부터 sqrt(N)으로 갈수록 a, b의 차이는 작아진다.

코드
두번째 방법이 속도면에서나 코드길이 면에서나 이득이 커보인다.

첫번째 방법

#include <iostream> #include <algorithm> #include <cmath> using namespace std; int G, L; bool isDisjoint(int a, int b) { for (int k=2; k<=sqrt(min(a, b)); k++) if (a%k == 0 && b%k == 0) return false; return true; } int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> G >> L; int T = L/G; for (int a=sqrt(T); a>=1; a--) if (T%a == 0) { int b = T/a, disjoint = true; if (isDisjoint(a, b)) { cout << a*G << ' ' << b*G; break; } }

두번째 방법

#include <iostream> #include <algorithm> using namespace std; int G, L, T, A, B; int gcd(int a, int b) {return b==0?a:gcd(b, a%b);} int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> G >> L; T = L/G; for (int a=1,b=T/a; a*a<=T; a++,b=T/a) if (T%a==0 && gcd(b, a)==1) A = a*G, B = b*G; cout << A << ' ' << B; }

댓글

댓글을 남기면 닉네임(또는 GitHub 계정 정보)이 저장됩니다. 개인정보처리방침

GitHub