JS Star 블로그

기억보다는 기록을✏️ 머신러닝, 웹개발, 물리학을 공부했고 계속 배워가고 있습니다.
📌 기존에 포스팅하던 블로그에서 포스트를 옮기는 중입니다.

[HackerRank] Non-Divisible Subset

01 Sep 2020 » algorithm

Non-Divisible Subset

https://www.hackerrank.com/challenges/non-divisible-subset/problem?isFullScreen=false

문제

숫자가 담긴 배열 S가 주어질 때, 요소 2개를 골라 합한 값이 k로 나누어 지지 않는 요소들로만 구성된 부분집합 S' 중 가장 긴 집합의 길이를 출력하라. S=[19, 10, 12, 10, 24, 25, 22] 이고 k=4 일 때, S'[0]=[10, 12, 25], S'[1]=[19, 22, 24]가 최대 길이의 부분집합이다.

풀이

일단 문제 이해하는 데에만 오랜시간이 걸렸다. 문제에서 S’[0] 안에 있는 요소들 중 어떤 두개의 요소를 골라서 합해도 4로 나누어 지지 않는다. S’[1]도 마찬가지이다. 다시 말하면 S에 있는 숫자로 집합을 만들라는 얘기다. 하지만 무식하게 조합을 사용하여 모든 경우의 수를 만드려하다 보면 시간초과가 날 것이라는 것을 짐작할 수 있다. 모든 알고리즘 문제는 규칙을 찾는 것이 중요하다.

두 개의 숫자를 더한 값이 4로 나누어 지지 않으려면 각각의 수를 4로 나눈 나머지를 더했을 때 4나 0이 되면 안된다. 예를들어 19와 22를 각각 4로 나눈 나머지는 3, 2이고 이는 4나 0이 아니므로 더한 값도 4로 나누어 떨어지지 않는다. 조금더 편하게 생각하기 위해 반대로 생각해보면 나머지의 합이 4가 되는 값 중 항상 하나만 선택하면 된다. 즉, 나머지가 1인 값이 들어가는 집합은 나머지가 3인 값이랑 함께 있으면 절대 안된다.

loop를 돌리면서 두 값 중 선택하면 된다. 더했을 때 4가 되는 경우의 수는 (1, 3), (2, 2) 이고, 1과 3 중 많은 요소를 더하고 2와 2는 함께 붙어있을 수 없으므로 무조건 한개만 추가할 수 있다. 또한 나머지가 0인 요소가 있다면 같은 이유로 한개만 추가할 수 있다.

def nonDivisibleSubset(k, s):
    # 주어진 집합에서 어떤 두개의 수를 골라 더했을 때 k로 나누어 떨어지지 않는 부분집합을 구하라
    new_s = [ss%k for ss in s]
    
    result = 0
    # 좌우로 중복되지 않은 곳까지만 검사 (k가 4이면 2까지, k가 5이면 2까지)
    # (1, k-1), (2, k-2), ...
    for i in range(1, k//2+1):
        # k가 4일 때, (2,2)와 같은 상황일 때, 2가 존재한다면 한번만 추가할 수 있다.
        if i == k-i and i in new_s: result += 1
        else:
            # 나머지의 합이 k가 되는 값 둘 중에 하나만 선택
            result += max(new_s.count(k-i), new_s.count(i))
    
    # 나머지가 0인 값이 있는 경우는 딱 하나만 선택할 수 있다.
    if 0 in new_s: result += 1

    return result

문제의 핵심은 특정 조건일 때, 선택할 수 있는 경우의 수가 명확하게 제한적이라는 것이다. 또한 정확한 경우의 수를 구하는 것이 아닌 최대를 구하는 것이므로 유연하게 풀 수 있다.