Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

파이썬으로 숫자 목록의 비트를 0에서 1로 k번 변경해 최소 합 구하기

숫자 목록 nums와 정수 k가 주어졌을 때, 다음 연산을 정확히 k번 수행한다고 가정해 보겠습니다. 목록에서 임의의 숫자를 하나 선택한 뒤, 그 숫자의 이진 표현에서 값이 0인 비트를 골라 1로 바꾸는 것입니다. 모든 연산을 마친 후 얻을 수 있는 최소 합계를 구해야 하며, 결과값이 너무 커질 경우 10^9+7로 나눈 나머지를 반환하면 됩니다.

예를 들어 입력이 nums = [4, 7, 3], k = 2라면 출력은 17이 됩니다. 4의 이진 표현은 100, 3은 011, 7은 111입니다. 두 개의 비트를 1로 바꿔야 하므로 4의 비트 두 개를 채워 111, 즉 7로 만들면 됩니다. 이때 전체 합은 7 + 7 + 3 = 17입니다.

접근 방법

비트를 0에서 1로 바꾸면 해당 숫자는 반드시 커지기 때문에, 합의 증가분을 최소화하려면 가장 낮은 자리, 즉 작은 2^i 값에 해당하는 0비트부터 채우는 것이 유리합니다. 따라서 모든 숫자를 낮은 비트 자리부터 차례로 검사하면서 0인 비트를 발견할 때마다 2^i를 더하고 k를 하나씩 줄여 나가는 그리디 방식으로 문제를 해결할 수 있습니다.

  • ans := 0, i := 0으로 초기화합니다.
  • k가 0이 아닌 동안 다음을 반복합니다.
    • nums의 각 원소 n에 대해 다음을 검사합니다.
      • n의 i번째 비트가 0이라면 ans에 2^i를 더하고 k를 1 감소시킵니다.
      • k가 0이 되면 내부 반복을 종료합니다.
  • 한 바퀴를 마치면 i를 1 증가시켜 다음 비트 자리를 검사합니다.
  • 최종적으로 (ans + nums의 모든 원소의 합) mod m을 반환합니다.

구현 예제

다음 코드를 통해 실제 구현을 확인해 보겠습니다.

class Solution:
    def solve(self, nums, k):
        m = 10 ** 9 + 7
        ans = 0
        i = 0
        while k:
            for n in nums:
                # n의 i번째 비트가 0인 경우
                if (n >> i) & 1 == 0:
                    ans += 1 << i   # 2^i만큼 합 증가
                    k -= 1
                    if k == 0:
                        break
            i += 1                  # 다음 비트 자리로 이동
        return (ans + sum(nums)) % m

ob = Solution()
nums = [4, 7, 3]
k = 2
print(ob.solve(nums, k))

입력

[4, 7, 3], 2

출력

17

동작 과정 살펴보기

i = 0일 때 4의 0번째 비트는 0이므로 ans에 1을 더하고 k는 1이 됩니다. 7과 3의 0번째 비트는 이미 1이므로 건너뜁니다. 이후 i = 1이 되면 4의 1번째 비트가 0이므로 ans에 2를 더하고 k가 0이 되어 반복이 종료됩니다. 최종적으로 (1 + 2 + 14) mod (10^9+7) = 17이 반환됩니다.

이 풀이는 각 비트 자리를 한 번씩만 훑으면 되기 때문에 효율적이며, 시프트 연산자 >><<를 활용해 이진수 문자열 변환 없이 빠르게 처리할 수 있다는 장점이 있습니다.