2961번: 도영이가 만든 맛있는 음식
첫째 줄에 재료의 개수 N(1 ≤ N ≤ 10)이 주어진다. 다음 N개 줄에는 그 재료의 신맛과 쓴맛이 공백으로 구분되어 주어진다. 모든 재료를 사용해서 요리를 만들었을 때, 그 요리의 신맛과 쓴맛은
www.acmicpc.net
💡풀이💡
요리를 할 때 N개의 재료들을 사용하거나 / 사용하지 않거나 해서 신맛과 쓴맛의 차이가 최소인 값을 찾는 문제이다.
- 가능한 재료들의 모든 집합을 구해야 하기 때문에 부분 집합
1. 입력 값
재료마다 신맛과 쓴맛의 값을 저장해야 했기 때문에 2차원 배열로 재료의 값을 저장했다.
그리고 어떤 재료가 선택 됐는지 알기 위해 boolean 1차원 배열을 생성했다.
static int N;
static int[][] flavors;
static boolean[] v;
static int min=1000000000;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
flavors = new int[N][2];
v = new boolean[N];
for(int i=0; i<N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
flavors[i][0] = Integer.parseInt(st.nextToken()); // 신맛
flavors[i][1] = Integer.parseInt(st.nextToken()); // 쓴맛
}
}
2. 부분 집합 구하기
- 해당 재료를 사용하는 경우
- 해당 재료를 사용하지 않는 경우로 나눠서 진행
📌 놓쳤었던 부분!
- 문제에서 보면 재료는 적어도 하나 사용해야 한다고 나와있다.
- 부분 집합을 구하게 되면 아무것도 선택되지 않은 공집합도 포함이 되는데 이때를 걸러내지 않아서 틀렸었다.
static void subset(int cnt) {
if(cnt == N) {
long f1 = 1; // 신맛
long f2 = 0; // 쓴맛
for(int i=0; i<N; i++) {
if(v[i]) {
f1 *= flavors[i][0];
f2 += flavors[i][1];
}
}
if(f1 == 1 && f2 == 0) return; // 아무것도 선택되지 않으면 return
min = Math.min(min, Math.abs(f1-f2));
return;
}
v[cnt] = true; // cnt번 재료를 사용할 때
subset(cnt+1);
v[cnt] = false; // cnt번 재료를 사용하지 않을 때
subset(cnt+1);
}
전체 코드
1) 부분 집합이 완성 됐을 때, 각 요리에 사용된 신맛, 쓴맛 계산하기
import java.io.*;
import java.util.*;
public class B2961 {
static int N;
static int[][] flavors;
static boolean[] v;
static long min=1000000000;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
flavors = new int[N][2];
v = new boolean[N];
for(int i=0; i<N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
flavors[i][0] = Integer.parseInt(st.nextToken()); // 신맛
flavors[i][1] = Integer.parseInt(st.nextToken()); // 쓴맛
}
subset(0);
System.out.println(min);
}
static void subset(int cnt) {
if(cnt == N) {
int f1 = 1; // 신맛
int f2 = 0; // 쓴맛
for(int i=0; i<N; i++) {
if(v[i]) {
f1 *= flavors[i][0];
f2 += flavors[i][1];
}
}
if(f1 == 1 && f2 == 0) return;
min = Math.min(min, Math.abs(f1-f2));
return;
}
v[cnt] = true;
subset(cnt+1);
v[cnt] = false;
subset(cnt+1);
}
}
2) 부분 집합을 구하는 재귀 함수의 매개변수로 각 재료의 신맛, 쓴맛 값 전달하기
package backjoon;
import java.io.*;
import java.util.*;
public class B2961 {
static int N;
static int[][] flavors;
static boolean[] v;
static int min=1000000000;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
flavors = new int[N][2];
v = new boolean[N];
for(int i=0; i<N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
flavors[i][0] = Integer.parseInt(st.nextToken()); // 신맛
flavors[i][1] = Integer.parseInt(st.nextToken()); // 쓴맛
}
subset(0, 1, 0);
System.out.println(min);
}
static void subset(int cnt, int f1, int f2) {
if(cnt == N) {
if(f1 == 1 && f2 == 0) return; // 공집합일 때(아무것도 선택 안 됐을 때) 처리
min = Math.min(min, Math.abs(f1-f2));
return;
}
v[cnt] = true;
subset(cnt+1, f1*flavors[cnt][0], f2 +flavors[cnt][1]);
v[cnt] = false;
subset(cnt+1, f1, f2);
}
}
정리
- 문제를 보면 모든 재료를 사용해서 요리를 만들었을 때, 그 요리의 신맛과 쓴맛은 모두 1,000,000,000(10억)보다 작은 양의 정수이다. 라고 되어있다. 그래서 신맛(f1), 쓴맛(f2) 계산하는 변수, 둘의 차의 최솟값을 저장하는 min이 int의 범위를 벗어나는 줄 알고 long으로 선언했었다. 근데 int의 범위가 20억이라고 굳이 long으로 안 해도 된다고 페어가 알려주셨다ㅎㅎㅎ 페어 짱짱맨👍👍😎😎
난... int 범위가 2억인줄 알았다...
2. 위에도 적었었지만 부분 집합을 구할 때는 공집합이 있을 수도 있다는 걸 까먹었다. 문제를 잘 읽어보고 부분 집합을 이 용할 때는 공집합일 때를 잘 처리해야겠다.
'알고리즘 문제풀이 > 백준' 카테고리의 다른 글
[백준 1012] 유기농 배추 (0) | 2023.02.12 |
---|---|
[백준 2164] 카드2 (0) | 2023.02.10 |
[백준 2798] 블랙잭 (0) | 2023.02.09 |
[백준 1244] 스위치 켜고 끄기 (0) | 2023.02.08 |
[백준 14889] 스타트와 링크 (0) | 2023.02.06 |
댓글