알고리즘 문제풀이/알고리즘 문제해결전략
11.5_알러지가 심한 친구들_ALLERGY
Treejin
2023. 3. 11. 22:20
반응형
문제 설명
algospot.com :: ALLERGY
알러지가 심한 친구들 문제 정보 문제 집들이에 n 명의 친구를 초대하려고 합니다. 할 줄 아는 m 가지의 음식 중 무엇을 대접해야 할까를 고민하는데, 친구들은 각각 알러지 때문에 못 먹는 음식
www.algospot.com
- n명의 친구를 초대한다.
- m가지의 음식 중 무엇을 대접할지를 고른다.
- 만들 줄 아는 음식의 목록과, 해당 음식을 못 먹는 친구들의 목록이 주어진다.
=> 각 친구가 먹을 수 있는 음식이 최소 하나씩은 있도록 하는 음식의 최소 개수를 출력하기
문제 풀이
자료구조 고르기
나는 이 문제에 비트마스킹을 이용했다.
각 요리마다 먹을 수 있는 친구의 수를 저장하는데,
주어지는 친구의 수(n)가 최대 50명이므로
64비트의 Long Long 자료형을 이용하면 비트마스킹으로 표현이 가능하다.
※ a, b, c, d 친구가 있을 때 c와 a가 먹을 수 있는 음식 : 0101(2)
친구 | a | b | c | d |
index | 0 | 1 | 2 | 3 |
음식(비트) | 1 | 0 | 1 | 0 |
이 때, 주의할 점은 C++에서 비트 연산을 수행할 때 32비트 연산을 수행하기 때문에,
1 << 45 와 같은 연산을 수행할때는
- (long long)1 << 45
- 1LL << 45
- 1i64 << 45
와 같이 형변환을 시켜주어야 한다.(몰라서 고생함...ㅠ)
음식 수 줄이기
각 음식 정보를 비트마스킹을 이용해서 저장할 때,
다른 음식과 비교해서 한 음식이 다른 음식을 대체할 수 있다면 해당 음식을 삭제해주었다.
두 음식을 or 연산한 결과로 두 음식 중 하나와 값이 동일하다면 대체할 수 있음을 의미하고 덮어씌웠다.
가지치기
음식을 고를 때,
- 고른 음식의 수가 최솟값보다 작다면 리턴하기
- 해당 음식을 고름으로써 음식을 먹을 수 있는 사람의 수가 늘어난다면 선택하기
과 같은 과정을 추가하여 더 빠르게 답을 찾을 수 있도록 가지치기를 수행했다.
문제 풀이 코드
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
using namespace std;
int n, m, minCnt;
long long success;
long long foods[50];
char friends[50][10];
// 친구 index 반환
int findFriendsIndex(char* name) {
for (int i = 0; i < n; ++i) {
bool find = true;
for (int j = 0; j < 10; ++j) {
if (name[j] == '\0' && friends[i][j] == '\0')
break;
if (name[j] != friends[i][j]) {
find = false;
break;
}
}
if (find)
return i;
}
}
// 음식 고르기
void choiceFood(int index, long long sum, int cnt) {
// 고른 음식의 수가 최솟값보다 커지면
if (cnt >= minCnt)
return;
// 답을 찾았다면
if (sum == success) {
minCnt = cnt;
return;
}
// 음식을 전부 확인했다면
if (index == m)
return;
// i번째 음식을 고름으로써 먹을 수 있는 사람이 늘어난다면
if ((sum | foods[index]) > sum) {
choiceFood(index + 1, sum | foods[index], cnt + 1);
}
choiceFood(index + 1, sum, cnt);
}
int main() {
int T;
cin >> T;
for (int tc = 1; tc <= T; ++tc) {
cin >> n >> m;
minCnt = m;
memset(foods, 0, sizeof(foods));
// 성공하는 경우 = 모든 친구가 먹는 경우 = n-1개의 비트가 모두 1인 경우
success = ((long long)1 << n) - 1;
for (int i = 0; i < n; ++i) {
cin >> friends[i];
}
for (int i = 0; i < m; ++i) {
int cnt;
cin >> cnt;
for (int j = 0; j < cnt; ++j) {
char name[10];
cin >> name;
int index = findFriendsIndex(name);
// index 번째 친구가 먹을 수 있음을 표기
foods[i] = foods[i] | ((long long)1 << index);
}
// 한쪽이 다른 한쪽을 대신할 수 있는 음식 제거
for (int j = 0; j < i; ++j) {
if ((foods[i] | foods[j]) == foods[i]) { // j가 i에 포함
foods[j] = foods[i];
foods[i] = 0;
--m;
--i;
break;
}
else if ((foods[i] | foods[j]) == foods[j]) { // i가 j에 포함
foods[i] = 0;
--m;
--i;
break;
}
}
}
choiceFood(0, 0, 0);
cout << minCnt << endl;
}
return 0;
}
반응형