포스트

[공부] 백준 11723 집합 C++

[공부] 백준 11723 집합 C++

📝 문제


🔍 문제 상황

문제 상황1 문제 상황2 문제 상황3


💡 해결 방법 / 접근 방식

  • 우선, 비어있는 공집합에 m번 동안 주어지는 연산(add, remove, check, toggle, all, empty)을 수행하는 문제입니다.
  • 해당 문제는 비트 마스킹 문제로써 비트 연산이 필요합니다.

비트연산(Bitwise Operation)

연산자이름의미예시 (a = 0101, b = 1100)
&AND둘 다 1일 때만 1a & b0100
` | `OR하나라도 1이면 1 
^XOR다르면 1a ^ b1001
~NOT0은 1로, 1은 0으로 반전~a → 반전된 a
<<왼쪽 시프트비트를 왼쪽으로 밀기a << 11010
>>오른쪽 시프트비트를 오른쪽으로 밀기a >> 10010

비트 마스킹(Bitwise Masking)

  • 비트 하나하나를 켜고(1) 끄고(0) 체크하는 것

  • 공집합 만들기
    1
    
    int s = 0;
    
  • s에 원소 추가
    1
    2
    3
    
    s |= (1<<3);
    // 1을 3간 왼쪽으로 시프트(이동) 후 OR 연산 후 s에 대입
    // 1 > 10 > 100 > 1000
    
  • s에 원소 삭제
    1
    2
    
    s &= ~(1<<3);
    // 1000 -> 0111로 변경 ("~"가 있기 때문) 후 AND 연산 후 s에 대입
    
  • 원소 존재 여부 파악
    1
    2
    3
    
    if(s & (1<<3){
      cout << "존재" << "\n";
    }
    
  • s에 모든 원소 채우기
    1
    2
    
    s |= (1<<8) - 1;
    // 10000 0000 - 1 = 1111 1111 식으로 채우기 가능
    
  • XOR
    1
    2
    
    s ^= (1<<3);
    // 3번째에 대한 XOR 연산 후 s에 대입
    

접근 방식

  • 위에서 파악한 비트 마스킹에 따라 각각의 입력 받은 문자열에 따른 연산을 진행합니다.
  • 문자열 뒤의 숫자(x)는 후에 입력 받아도 되기에 문자열로 입력 받은 후, 입력을 받습니다.
  • 주의할 점은 비트 연산과 일반 정수 연산은 다르기에 if (s == x) 와 같이 같은 값이 있는지 비교한다고 사용한다면 안됩니다.
    • 이 조건은 s라는 전체 비트 상태와 x라는 단일 숫자가 완전히 같을 때만 참이 됩니다.
    • 하지만 우리가 원하는 건 x번째 비트가 켜져 있는지만 확인하는 것 입니다.
      • s == x가 참인 경우
        1
        2
        3
        
          s = 8;  // => 000...01000
          x = 8;  // => 000...01000
        
        
      • 문제에서 원하는 것
        1
        2
        3
        
          // x번째 비트가 켜져있는지만 확인하고 싶다
          if (s & (1 << x)) // x번째 비트가 1이면 true
        
        

🛠️ 구현 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 비트마스킹 문제 <- 비트 연산 필요

#include <iostream>
#include <string>

using namespace std;

int m, x;
string str;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    int s = 0; // 공집합
    cin >> m;

    for (int i = 0; i < m; ++i) {
        cin >> str;
        if (str == "add") {
            cin >> x;
            s |= (1 << x); // s집합에 원소를 추가
        } else if (str == "check") {
            cin >> x;
            if (s & (1 << x)) {
                // s에 x가 있으면
                cout << "1" << "\n";
            } else {
                // s에 x가 없으면
                cout << "0" << "\n";
            }
        } else if (str == "remove") {
            cin >> x;
            s &= ~(1 << x); // s집합에 원소를 삭제
        } else if (str == "toggle") {
            cin >> x;
            s ^= (1 << x); // xor, 0 -> 1, 1 -> 0
        } else if (str == "all") {
            s = (1 << 21) - 1; // s에 모든 원소 채우기(1~20)
        } else if (str == "empty") {
            s = 0; // 공집합 화 시키기 == (s = 0)
        }
    }
    return 0;
}

🧷 결론

  • 배열을 사용해서 푸는 방법도 있지만, 비트 마스킹과 비트 연산을 사용해서 바로 푸는 방법도 있어서 이 방법을 시도해보았습니다.
  • 비트 마스킹에 대해 설명이 잘 되어있는 블로그들이 있어서 이를 참고하면서 풀이를 진행하였더니 수월하게 풀어나갈 수 있었습니다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

인기 태그