알고리즘_PS/Baekjoon

[백준] 22943_수 / python

hanseongjun 2022. 4. 5. 21:04
728x90
반응형

https://www.acmicpc.net/problem/22943https://www.acmicpc.net/problem/22943

 

22943번: 수

0부터 9까지 $K$가지의 숫자를 한 번씩만 사용하여 만들 수 있는 수 중 아래 조건을 모두 만족하는 수들의 개수를 구해보자. 단, 수의 맨 앞에는 0이 올 수 없다. 즉, 0143는 불가능하다. 서로 다른

www.acmicpc.net


문제

0부터 9까지 K가지의 숫자를 한 번씩만 사용하여 만들 수 있는 수 중 아래 조건을 모두 만족하는 수들의 개수를 구해보자. 단, 수의 맨 앞에는 0이 올 수 없다. 즉, 0143는 불가능하다.

  1. 서로 다른 두 개의 소수의 합으로 나타낼 수 있는 경우
  2.  M으로 나누어 떨어지지 않을때까지 나눈 수가 두 개의 소수의 곱인 경우, 이 때, 두 개의 소수가 같아도 된다.

예를 들어, K가 1이고 M이 11인 경우로 생각해보자. 한자리 수 중 1번 조건을 만족하는 수는 5, 7, 8, 9이고 2번 조건을 만족하는 수는 4, 6, 9가 있다. 이 두 개의 조건을 둘 다 만족하는 수는 9이므로 이 경우에는 1개이다.

입력

첫 번째 줄에 K와 M 주어진다.

출력

2가지 조건을 만족하는 수의 개수를 출력한다.

제한

  • 은 정수

여기서 소수가 정말 많이 쓰이는데, 일단 정말 소수가 많이 쓰이므로 나는 에라토스테네스의 체로 소수 리스트를 만들어 두고, 거기서 소수가 있는지 체크를 하는 방식으로 알고리즘을 구현했다.

 

 

 

- 그리고, 소수 리스트를 만들 때 set 자료형으로 구현해 소수 리스트 안에 있는지 판단하는 연산에 걸리는 시간 복잡도를 줄였다.

 

- itertools의 permutations을 사용하여 조합을 구현했으며, 첫 자리가 0인 경우는 continue문을 사용해 걸러 주었다.

 

- 또한, 조건2를 먼저 체크하고 조건 1을 확인하는 식으로 했는데, 이는 조건 2를 체크할 때 m으로 나누어 연산을 진행하므로 아무래도 수를 조금이나마 크기를 줄여 확인해 더 확인하기 쉽게 했고, 조건 1에서 소수의 합이 되는 경우를 찾는 것보다 소수인 약수를 구하는 게 소수 리스트를 활용하여 할 수 있어서 더 빠르게 구현이 가능했기 때문이다.

 

코드는 다음과 같다.

from itertools import permutations
k, m = map(int, input().split())
lst = []
MAX = 98765 // 10**(5-k)    # K개의 숫자를 고를 때 나올 수 있는 가장 큰 수

# 일단 최대 범위까지 소수 리스트 만들어 놓음
check = [0] * (MAX + 1)
prime_lst = set()   # 있는지 체크하기에는 set 자료형이 최고
for i in range(2, MAX+1):
    if check[i] == 0:
        check[i] = 1
        prime_lst.add(i)  # 1부터 N까지 소수 리스트
        j = 1
        while i * j <= MAX:
            check[i*j] = 1
            j+=1

# k개의 숫자 조합
for num in permutations(range(10), k):
    if num[0] == 0: # 첫 자리가 0이면 취급 안함
        continue
    N = int(''.join(list(map(str, num))))   # 숫자 조합
    temp = N
    while temp % m == 0:    # m으로 나누어떨어지지 않을 때까지 나눠줌
        temp //= m
    i = 2
    temp_lst = []
    while i <= int(temp**0.5):  
        if temp % i == 0:   # 한 번이라도 소수와 소수가 아닌 수가 있어도 반례이므로 break 해줌
            if i in prime_lst and temp//i in prime_lst: # 2조건
                j = 2
                while j < N // 2:  # 1조건
                    if j in prime_lst and N-j in prime_lst:
                        lst.append(N)
                        break
                    j += 1
            break   #?
        i += 1
                    
print(len(lst))

 

처음에는 단지 리스트로 구현을 했다 시간이 너무 오래 걸려 한번 조건 부분을 손보고 리스트로 set으로 바꾸니 훨씬 연산이 빨라졌다.

시간 복잡도 개선을 위해서 set이나 deque자료형을 쓰는 것을 아끼지 말아야겠다. 

728x90
반응형
LIST