비어있는 공집합 S가 주어졌을 때, 아래 연산을 수행하는 프로그램을 작성하시오.
- add x: S에 x를 추가한다. (1 ≤ x ≤ 20) S에 x가 이미 있는 경우에는 연산을 무시한다.
- remove x: S에서 x를 제거한다. (1 ≤ x ≤ 20) S에 x가 없는 경우에는 연산을 무시한다.
- check x: S에 x가 있으면 1을, 없으면 0을 출력한다. (1 ≤ x ≤ 20)
- toggle x: S에 x가 있으면 x를 제거하고, 없으면 x를 추가한다. (1 ≤ x ≤ 20)
- all: S를 {1, 2, ..., 20} 으로 바꾼다.
- empty: S를 공집합으로 바꾼다.
구현
비트마스킹
이 문제를 보고 트리 혹은 배열로 풀 수 있다고 생각했었지만, all
과 empty
의 연산을 보고 안 되겠다 싶었다. 트리 혹은 배열로 구현하여 all
과 empty
를 구현하려면 for
문을 0
에서부터 20
번 돌아야되는데, m
의 최댓값이 3,000,000
이므로 너무 오래 걸린다. 즉, 비트마스킹
을 이용해야 한다.
1
번부터 20
번까지의 수를 각각 비트로 대응시킨다. 맨 오른쪽 1
번 비트가 1
이면 s
에 1
이 있다는 거고, 0
이면 없다는 뜻이 된다. 즉 all
의 연산을 할 경우에는 s
에 1^20-1(1,048,575)
을 대입시키기만 하면 되고, empty
의 경우 s
에 0
을 대입하면 된다.
add
의 경우 해당 비트값만 1
로 주고, 비트 OR
연산을 수행하면 된다. remove
는 해당 비트값을 1
로 준 뒤, NOT
연산으로 뒤집고, (100
-> 011
로 만드는 작업) 해당 결과값과 s
를 AND
연산 하면 된다.
check
는 그냥 해당 비트만 1
로 주고, 그 값과 s
로 AND
연산을 수행하면 된다. 해당 비트(k
번째)에 값이 있을 경우, 1 << k
의 값이 되고, 값이 없으면 0
이 되는 것을 이용하여 판별한다. toggle
은 해당 비트를 1
로 주는 연산을 한 뒤, s
와의 XOR
연산으로 해결한다.
각 k
번째의 비트가 k
의 포함 유무라고 생각하면 된다.
단순히 std::cin
을 사용하면 시간초과가 나서 char[]
을 이용하였다.
#include <stdio.h>
#include <cstring>
int main()
{
int s = 0, m, par;
char in[10];
scanf("%d", &m);
while (m--) {
scanf("%s", in);
if (!strcmp(in, "add")) {
scanf("%d", &par);
s |= 1 << (par - 1);
}
else if (!strcmp(in, "remove")) {
scanf("%d", &par);
s &= ~(1 << (par - 1));
}
else if (!strcmp(in, "check")) {
scanf("%d", &par);
printf("%d\n", (s & (1 << (par - 1))) != 0);
}
else if (!strcmp(in, "toggle")) {
scanf("%d", &par);
s ^= 1 << (par - 1);
}
else if (!strcmp(in, "all")) s = 1048575;
else s = 0;
}
return 0;
}