괜찮은 아이디어가 하나 있다.
#1676번 팩토리얼 0의 개수 https://www.acmicpc.net/problem/1676
1676번: 팩토리얼 0의 개수
N!에서 뒤에서부터 처음 0이 아닌 숫자가 나올 때까지 0의 개수를 구하는 프로그램을 작성하시오.
www.acmicpc.net
입력된 수의 팩토리얼을 구하고, 뒤에서부터 0이 아닌 수가 나올 때 까지 0의 개수를 구하는 문제이다.
어떻게 보면 비교적 순진하게 풀었다.
#1676번 #파이썬
num = int(input())
if num == 0:
print(0)
else:
for i in range(2, num):
num *= i
loop = str(num)
answer = 0
for s in range(len(loop)-1, -1, -1):
if loop[s] != "0":
break
else:
answer += 1
print(answer)
그냥 팩토리얼 구하고, 스트링 역전시켜서 0 개수 구하기..
정말 순진한 코드
그리고 나서
#2004번 조합 0의 개수 https://www.acmicpc.net/problem/2004
2004번: 조합 0의 개수
첫째 줄에 정수 $n$, $m$ ($0 \le m \le n \le 2,000,000,000$, $n \ne 0$)이 들어온다.
www.acmicpc.net
를 풀기 시작했는데, 전혀 감이 잡히지 않았다. 검색 끝에 1676번과 비슷한 방법으로 풀면 된다는 사실을 알게 됐는데 왠걸 뭔 개소린지...
알고보니 내가 1676번도 효율이 떨어지는 방법으로 풀었던 것이었다.
맨 뒤(1의 자리)부터 0의 개수라는건 결국 그 수를 구성하는 인수 중 10의 개수를 묻는 것이었다.
죽 2*5의 개수를 세어주면 된다.
2004번에서 조합을 구할 때에는 총 팩토리얼 함수를 3번 사용해야 하는데, 수가 커질수록 시간은 엄청나게 소모된다. 당연히 팩토리얼을 다 구하고 있으면 시간초과가 난다.
n 팩토리얼 = n이하의 수의 곱 이니까,
n이하의 모든 수에서 2와 5의 개수를 구해주면 된다.
이 또한 신박한 방법이 있어서 정말 놀라웠다.
무슨 말인지 이해가 됐으면 좋겠다.
10! = 10 9 8 7 6 5 4 3 2 1 의 곱인데,
1) 이 중 2를 가지고 있는 수는 총 5개이다. 10을 2로 나눴을 때의 정수값과 같다.
2) 다음으로 4(2의 2제곱)을 가지고 있는 수는 총 2개이다. 이는 1)에서 나온 정수값(개수)를 다시 2로 나눴을 때의 정수값과 같다.
3) 다음으로 8(2의 3제곱)을 가지고 있는 수는 총 1개이다. 이는 2)에서 나온 정수값(개수)를 다시 2로 나눴을 때의 정수값과 같다.
4) 3)의 정수값을 다시 2로 나누면 0이다. 즉 더 이상의 2는 없다.
결론) 10팩토리얼에는 2가 8개 들어있다.
이걸 뭐라고 표현해야 할지 모르겠지만, 이 이론은 모든 수에 동일하게 적용된다.
코드로 표현하면 다음과 같다.
def count_num_of_factorial(num, n):
count = 0
while num != 0:
num //= n
count += num
return count
num팩토리얼의 인자 중 에서 n의 개수를 구하는 함수라고 보면 된다.
이 방식을 2004번 조합의 개수에 적용하면
#2004번 #파이썬
num1, num2 = map(int, input().split())
nums = [num1, num2, num1-num2]
def count_five(num):
count = 0
while num != 0:
num //= 5
count += num
return count
def count_two(num):
count = 0
while num != 0:
num //= 2
count += num
return count
two = [count_two(x) for x in nums]
five = [count_five(x) for x in nums]
two = two[0] - two[1] - two[2]
five = five[0] - five[1] - five[2]
print(min(two, five))
이 된다.
조합의 경우에는 nCm = n! / m! * (n-m)! (n>m) 이니까 각각의 수 (n, m, n-m)에서 2와 5의 개수를 구해준다.
n팩토리얼을 나머지 두 수로 나누는 구조이기 때문에 지수계산 상에서는 뺄셈이 된다.
따라서 n!의 2와 5의 개수에서 나머지 두 수의 2와 5의 개수를 빼주면 된다.
10이 되기 위해서는 2와 5의 개수가 동일해야 함으로 2와 5의 개수중 작은 값이 10의 개수 즉 1의자리 부터 0의 개수가 된다.
완성.
이 이론을 1676번 문제에도 적용을 시켜보자
#1676번 파이썬
num = int(input())
count = 0
while num != 0:
num //= 5
count += num
print(count)
개쉽다.
의문이 들 수 있다. 왜 1676에서는 5의 개수만 세어줬냐고.. 보통의 팩토리얼에서는 2의 개수가 5보다 많다. 즉 항상 5의 개수가 더 작기 때문에 5만 세어줘도 정답이 된다. 다만 2004번에서는 지수상에서의 뺄셈이 존재하기 때문에 위 사실을 확신할 수 없어서 min함수 연산을 추가해준 것이다.
휴 오늘도 힘들었다.
'Algorithm > acmicpc.net' 카테고리의 다른 글
진수 계산법 (0) | 2021.12.04 |
---|---|
최대공약수 GCD(Greatest Common Division) (0) | 2021.12.02 |
익숙해지기 참 어려운 소수 (0) | 2021.11.28 |
EOFerror : 입력이 끝날 때 종료를 원한다면!.. (0) | 2021.11.25 |
후위 표기식 (0) | 2021.11.24 |