문제 풀이/문제 풀이(프로그래머스)

연습문제 > 다음 큰 숫자

풀뿌리 2024. 12. 5. 09:52

문제 위치 : https://school.programmers.co.kr/learn/courses/30/lessons/12911

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

builtin_popcount라는 함수를 사용하여 풀었다.

이는 이진수 변환시의 1의 갯수를 새어주는 함수이다.

using namespace std;

int solution(int n) {
    int c = n;
    int count = 0;
    
    while (c) {
        c &= (c - 1);  
        count++;
    }

    int p = n;
    while (true) {
        p++;
        if (__builtin_popcount(p) == count) {
            return p;
        }
    }
}

 

다른 함수를 사용한 유사한 풀이는 다음과 같다.

 

#include <bitset>

using namespace std;

int solution(int n) {
    int num = bitset<20>(n).count();

    while (bitset<20>(++n).count() != num);
    return n;
}

 

근데 위와 같이 이를 사용하는 것 외에 비트 연사자를 사용하는 풀이가 있었다.

 

int solution(int n) {
    int pivot = n & -n;
    int before = ((n ^ (n + pivot)) / pivot) >> 2;
    return (n + pivot) | before;
}

 

정말 간단하고 명료하다.

 

한줄 씩 설명하자면

int pivot = n & -n;

 

부분이다. -n은 n의 2의 보수이다.

그리고 이를 & 연산하게 되면 가장 오른쪽에 있는 1의 위치를 얻을 수 있게 된다.

이는 보수의 성질을 이용한 것이다. 이 부분이 너무 깔끔했다.

 

나 또한 가장 작은 1의 위치를 구하는 방식을 생각했지만 나는 문자열로 변환하여 풀 생각을 하다 실패하였기 때문이다.

 

그 다음은 before를 구하는거다.

int before = ((n ^ (n + pivot)) / pivot) >> 2;

 

먼저 pivot+n이다.

 

이는 이진 표현에서 pivot위치에 1을 더해준 것과 같은 연산이다.

 

바로 다음 큰 수인데 왜 1이 있는 위치에 1을 더하냐고 생각할 수도 있는데 이는 0이 있는 위치에 연산하는 단계가 무의미하기 때문이다. 

 

문제 조건으로는 이진 표현하였을 때 1의 갯수가 같다 라는 조건이 붙어있다.

 

그 말인 즉슨 0의 위치에 1을 더해도 1의 갯수가 변경되므로 해당 값은 절대 정답이 될 수 없다.

 

그렇기 때문에 다음 큰 값은 최소한 pivot의 위치에 1을 더한 것보다는 항상 크다는 뜻이며 우리가 이를 기준으로 삼을 수 있는 이유이다.

 

그 다음으로는 n ^ (n + pivot)이다.

 

이는 둘의 XOR의 결과이다.

 

n = 78 (1001110), n + pivot = 80 (1010000)일 때, XOR 연산 결과는

n = 78    ->  1001110
n + pivot = 80  ->  1010000
--------------------------------
n ^ (n + pivot)  ->  0011110

 

다음과 같이 이루어진다.

 

즉, 변경된 비트들의 위치가 0011110으로 나타나는 것이다.

 

그리고 문제에서는 이를 계산하는데 있어 가장 작은 다음 큰수를 고르는 것이다.

 

before는 나중에 n+pivot의 1의 갯수를 보정해주는데 사용되므로 pivot을 통한 상대 위치를 골라줘야 한다.

 

따라서

 

/ pivot 연산이 들어간다.

 

(n ^ (n + pivot)) / pivot 의 계산 결과는 

0011110 / 0000010 = 0001111
  •  

같은 식으로 1들의 위치가 가장 작은 0이 되도록 맞춰주는 것이다.

 

그 다음 >>2 연산을 해준다.

 

이는 before가 조정해주는 비트의 위치들이 n + pivot과 n사이에 존재해야기 때문에 조정해주는 것이다. 

 

마지막 

return (n + pivot) | before;

 

연산은 before가 가지고 있는 비트 정보를 바탕으로 n+pivot에 부족한 1의 갯수를 보정해주는 것이다.