제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
SWEA 3421 수제 버거 장인입니다.
포인트
중·고등학교 때 배운 부분집합 개념을 떠올려 보면, 원소의 개수가 N개일 때 가능한 모든 부분집합의 수는 2ⁿ임을 알 수 있습니다.
이 문제에서도 모든 음식 조합(부분집합)을 고려하되, 서로 같이 먹으면 안 되는 음식 쌍(충돌 쌍)이 포함된 조합은 제외해야 합니다.
초기에는 DFS를 이용해 완전탐색을 시도했지만, 시간 초과가 발생하였고, 더 빠른 탐색 방식이 필요했습니다.
그 대안으로 선택한 것이 비트마스킹(bit masking)입니다.
비트마스킹은 집합의 포함 여부를 비트(0 또는 1)로 표현할 수 있기 때문에, 모든 조합을 효율적으로 순회할 수 있으며,
AND(&) 연산을 통해 충돌 여부도 매우 빠르게 판단할 수 있습니다.
이처럼 비트 연산은 속도가 빠르고, 메모리 효율도 뛰어나기 때문에,
이번 문제와 같이 부분집합을 다루는 경우에 매우 적합한 방법입니다.
소스코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Solution {
private static int tc, N, M;
private static boolean[][] disabled;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
tc = Integer.parseInt(br.readLine());
StringBuilder sb = new StringBuilder();
StringTokenizer st;
for (int t = 1; t <= tc; t++) {
st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());
long[][] conflict = new long[M][2];
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
long a = 1 << (Integer.parseInt(st.nextToken()) - 1);
long b = 1 << (Integer.parseInt(st.nextToken()) - 1);
conflict[i][0] = a;
conflict[i][1] = b;
}
long count = 0;
for (int i = 0; i < (1 << N); i++) {
boolean check = false;
for (int j = 0; j < M; j++) {
long foodA = conflict[j][0];
long foodB = conflict[j][1];
if ((i & foodA) != 0 && (i & foodB) != 0) {
check = true;
break;
}
}
if (!check) {
count++;
}
}
sb.append("#").append(t).append(" ").append(count).append("\n");
}
System.out.println(sb);
}
}
/*
3
3 2
1 2
2 3
3 0
3 3
1 2
1 3
2 3
*/
코드 설명
- long[][] conflict
서로 함께 먹을 수 없는 음식 쌍을 저장하는 배열- 1 << (번호 - 1) 형태로 비트 마스크로 변환하여 저장합니다.
(예: 음식 1 → 0001, 음식 3 → 0100)
- 1 << (번호 - 1) 형태로 비트 마스크로 변환하여 저장합니다.
for (int i = 0; i < (1 << N); i++) {
boolean check = false;
for (int j = 0; j < M; j++) {
long foodA = conflict[j][0];
long foodB = conflict[j][1];
if ((i & foodA) != 0 && (i & foodB) != 0) {
check = true;
break;
}
}
if (!check) {
count++;
}
}
- i는 0부터 2^N - 1까지의 모든 정수를 나타내며, 각 숫자가 하나의 부분집합을 의미합니다.
- 예: i = 5 (101₂) → 음식 1과 음식 3을 선택한 경우
- conflict에 저장된 음식 쌍이 동시에 부분집합에 존재하면 해당 조합은 제외
- (i & foodA) != 0 && (i & foodB) != 0 → 두 음식 모두 포함되었는지 확인
- 조건을 만족하는 조합(check == false)만 count++
'코딩테스트 > SWExpert' 카테고리의 다른 글
[코딩테스트] SWEA 1812 수정이의 타일 자르기(Java) (0) | 2025.07.03 |
---|---|
[코딩테스트] SWEA 5521 상원이의 생일파티(Java) (2) | 2025.07.03 |
[코딩테스트] SWEA 6782 현주가 좋아하는 제곱근 놀이(Java) (0) | 2025.07.02 |
[코딩테스트] SWEA 7793 오! 나의 여신님(Java) (2) | 2025.07.02 |
[코딩테스트] SWEA 1256 K번째 접미어(Java) (0) | 2025.07.01 |