문제
윤화와 준희는 솔선수범하여 쓰레기를 줍는 착한 일을 하였다. 원장선생님께서는 윤화와 준희를 칭찬하시고 과자나 사 먹으라고 하시며 동전 몇 개를 윤화와 준희에게 건네 주었다.
그런데 돈을 받은 윤화와 준희는 좋아하기보다 고민에 빠지고 말았다. 원장선생님께 받은 이 돈을 어떻게 나누어 할지 고민에 빠진 것이다. 두 사람 모두 상대방이 자기보다 1원이라도 더 받는 것은 도저히 인정할 수 없어 한다. 따라서 돈을 똑같이 둘로 나누어 가져야 두 사람이 모두 만족할 수 있게 된다.
하지만 두 사람에게 돈을 똑같이 나누는 것이 불가능한 경우도 있다. 예를 들어 500원짜리 1개와 50원짜리 1개를 받았다면, 이 돈을 두 사람이 똑같이 나누어 가질 수는 없다. 물론 동전을 반으로 잘라서 나누어 가질 수도 있겠지만 그러면 돈으로서의 가치를 잃기 때문에 그렇게 할 수는 없다.
이제 우리가 할 일은 다음과 같다. 원장 선생님께서 N가지 종류의 동전을 각각 몇 개씩 주셨을 때, 그 돈을 반으로 나눌 수 있는지 없는지 판단하는 것이다.
입력
세 개의 입력이 주어진다. 각 입력의 첫째 줄에 동전의 종류 N(1≤N≤100)이 주어진다. 각 입력의 둘째 줄부터 N+1째 줄까지 각각의 동전의 금액과 개수가 빈 칸을 사이에 두고 주어진다. 단, 원장선생님께서 주신 금액의 총 합은 100,000원을 넘지 않는다.
출력
첫째 줄부터 세 줄에 걸쳐, 각 입력에 대하여 반으로 나누는 것이 가능하면 1, 불가능하면 0을 출력한다.
예제 입력 1
2
500 1
50 1
3
100 2
50 1
10 5
3
1 1
2 1
3 1
예제 출력 1
0
1
1
풀이 .
import java.io.*;
import java.util.*;
class Coin {
int value, quantity;
Coin(int value, int quantity) {
this.value = value;
this.quantity = quantity;
}
}
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = null;
int T = 3;
while(T-- > 0) {
int n = Integer.parseInt(br.readLine());
Coin[] coins = new Coin[n+1]; // 원장님이 주는 금액의 최대는 10만원
boolean[] dp = new boolean[100001]; // i원 만들기 가능?
int total = 0;
for(int i = 1; i <= n; i++) {
st = new StringTokenizer(br.readLine());
int value = Integer.parseInt(st.nextToken());
int quantity = Integer.parseInt(st.nextToken());
coins[i] = new Coin(value, quantity);
total += value * quantity; // 받은 총 금액 구함
for(int j = 1; j <= quantity; j++) {
dp[value * j] = true; // 각 종류의 동전으로 만들 수 있는 액수 먼저 체크
}
}
// 굳이 dp[] 다 안 채워도 되는 경우
if(total % 2 == 1) {
System.out.println(0);
continue;
}else if(dp[total / 2]) {
System.out.println(1);
continue;
}
// 주어진 동전으로 (total / 2)원을 만들 수 있는지 확인하면 될 듯?
dp[0] = true;
for(int i = 1; i <= n; i++) {
int v = coins[i].value;
int q = coins[i].quantity;
for(int j = total/2; j >= v; j--) {
if(dp[j - v]) { // dp[j-v]가 가능해야 거기에 동전 하나씩 더해서 다음 액수를 가능하게 만들지
// (j-v)원부터 동전 v를 하나씩 사용하는 것
for(int k = 1; k <= q; k++) {
if(j - v + v * k > total/2) break; // dp[total/2] 이상으로는 어차피 채울 필요 없음
dp[j - v + v * k] = true;
}
// // 이게 이말임
// for(int k = 0; k < quantity; k++) {
// dp[j + value * k] = true;
// }
}
}
}
System.out.println(dp[total / 2] ? 1 : 0);
}
}
}
DP로 푸는 냅색 문제.
동전을 하나씩 사용하여 total/2 ~ v에 대한 검사를 한다.
1. 0원까지 안 하고 v까지만 검사하는 이유?
해당 동전을 1개만 사용해도 v-1원보다는 크니까
2. v~total/2가 아니라 total/2~v로 내려오면서 검사하는 이유?
냅색 문제에서 흔히 발생하는 오류를 피하기 위함.
오름차순으로 검사를 하면 이전에 이미 사용한 동전에 의해 true가 된 것을 가지고 동전을 또 사용해서 다음값을 true로 만든다.
결국 1원짜리 하나만 있어도 모든 dp[]가 true가 되어버린다.
이것을 막기 위해 큰 금액부터 검사.
3. if(dp[j - v]) 하는 이유?
v의 값을 가지는 동전 q개를 사용하려는 상황.
dp[j-v] 까지는 완성이 되어있어야 동전 1~q개를 사용하면서 dp[j], dp[j+v], ... , dp[j + (q-1)v]을 만들 수 있다.
dp 테이블에 대한 정의도 어려웠고 채우는 것도 어려웠다.
그냥 다 어려웠다..
'알고리즘 문제 > 백준 온라인 저지' 카테고리의 다른 글
[BOJ] 1920 - 수 찾기 JAVA (0) | 2021.03.07 |
---|---|
[BOJ] 1874 - 스택 수열 JAVA (0) | 2021.03.07 |
[BOJ] 12865 - 평범한 배낭 JAVA (0) | 2021.02.28 |
[BOJ] 1149 - RGB 거리 JAVA (0) | 2021.02.25 |
[BOJ] 1662 - 압축 JAVA (0) | 2021.02.25 |