물병은 같은 양의 물을 가진 물병 2개만을 합칠 수 있다.
1, 2, 4, 8, 16, ...
과 같은 2^n형태의 용량을 가질 수 있게 된다.
이는 2진수로 1, 10, 100, ...
과 같은 형태이다.
즉 목표 물병의 수
가 1개라면,
10000...
1로 시작하고 0개 이상의 0으로 이루어진 2진수 중 가장 작은 수를 만들면 된다.
그럼 1보다 많다면 어떻게 해야 할까?
예를 들어 목표 물병의 수
가 3개라면,
10000, 100, 1
이런 식으로 3개의 10... 형태의 2진수를 만들 수 있도록 하면 된다.
해당 2진수들을 모두 합치면 10101
이 되고,
1이 3개 포함된 2진수가 된다.
그렇다면 현재 물병의 수
를 2진수로 표현한 후,
목표 물병의 수
만큼의 1이 포함된 가장 작은 2진수를 만든다면 될 것이라는 생각이 든다.
1, 1, 1
하지만 위와 같이 3개의 2진수를 만든다면,
그 합은 11
이 된다.
즉, 10
은 1개도 될 수 있고 2개도 될 수 있다.
100
은 4개까지도 될 수 있다.
111
의 경우,
100, 10, 1
: 3개,
100, 1, 1, 1
: 4개,
10, 10, 1, 1, 1
: 5개,
10, 1, 1, 1, 1, 1
: 6개,
1, 1, 1, 1, 1, 1, 1
: 7개,
7개까지 쪼개질 수 있다.
여기서 111
은 10진수로 7
이다.
최대 해당 10진수만큼 쪼갤 수 있다.
하지만 2개는 될 수 없다.
2진수에 포함된 1의 개수 이하의 물병은 만들 수 없는 것이다.
그렇다면 이러한 성질을 이용하여 문제 해결 방법을 찾는다.
- n은 최대 n개까지 쪼갤 수 있다.
- 2진수에 포함된 1의 개수 이하의 물병은 만들 수 없다.
만약 현재 물병의 수
가 목표 물병의 수
보다 작거나 같다면, 0
출력 후 종료
만약 현재 물병의 수
가 목표 물병의 수
보다 많다면,
해당 2진수가 목표 물병의 수
보다 적은 수의 1
을 가졌더라도 필요한 만큼 쪼갤 수 있다.
0
출력 후 종료
해당 2진수가 목표 물병의 수
보다 많은 수의 1
을 가지고 있다면,
1의 개수가 목표 물병의 수
와 같거나 작을 때까지 값을 증가시킨다.
K = 2, 1011 --> 1100
K = 2, 1101 --> 1110 --> 10000
if (N <= K) {
System.out.println(0);
return;
}
...
if (countOne(binaryValue) <= K) {
System.out.println(0);
return;
}
subtractOne();
System.out.println(getDiff());
Java
에는 Integer.toBinaryString()
이라는 메소드가 존재한다.
해당 메소드는 숫자를 간단히 2진수를 표현한 문자열로 바꿔준다.
2진수의 자릿수를 변환해주어야 하기 때문에 해당 문자열을 배열로 변환하였다.
1100
을 10000
으로 만들어야 하는 경우, 배열의 크기가 변할 수 있다.
이러한 경우를 대비하기 위해 배열로 변환할 때 해당 문자열의 크기를 기준으로 하지 않고, 고정된 값을 기준으로 두었다.
N
의 최댓값인 10000000
을 2진수로 표현하여도 30자리를 넘지 않는다.
private static final int LENGTH = 30;
private static int[] binaryValue = new int[LENGTH];
그렇기 때문에 2진수를 저장할 배열의 크기를 30으로 지정하였다.
1
이 너무 많을 경우, 1
을 줄여야 한다.
낮은 자리부터 해당 자리의 수가 1
이라면 1
을 더해 더 높은 자리로 올려버린다.
이를 반복하면 1
의 수를 점차 줄일 수 있다.
올림수를 활용하였을 때, 2
가 생겼다면, 1
의 개수가 하나 줄어든 것이다.
101 -> 110 -> 200 -> 1000
private static void subtractOne() {
int oneCnt = countOne(binaryValue);
int i = LENGTH - 1;
while (oneCnt > K) {
if (binaryValue[i] == 1) {
binaryValue[i] = 0;
binaryValue[i-1]++;
for (int j=i-1; binaryValue[j] == 2; j--) {
binaryValue[j] = 0;
binaryValue[j-1]++;
oneCnt--;
}
}
i--;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
public class _1052 {
private static final int LENGTH = 30;
private static int N, K;
private static int[] binaryValue = new int[LENGTH];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
if (N <= K) {
System.out.println(0);
return;
}
String binaryN = Integer.toBinaryString(N);
Arrays.fill(binaryValue, 0);
int binaryNLength = binaryN.length();
for (int i=0; i<binaryNLength; i++) {
binaryValue[LENGTH - binaryNLength + i] = binaryN.charAt(i) - '0';
}
if (countOne(binaryValue) <= K) {
System.out.println(0);
return;
}
// System.out.println(Arrays.stream(binaryValue).mapToObj(String::valueOf).collect(Collectors.joining()));
subtractOne();
// System.out.println(Arrays.stream(binaryValue).mapToObj(String::valueOf).collect(Collectors.joining()));
System.out.println(getDiff());
}
private static int countOne(int[] binaryValue) {
int result = 0;
for (int value : binaryValue) {
result += value;
}
return result;
}
private static void subtractOne() {
int oneCnt = countOne(binaryValue);
int i = LENGTH - 1;
while (oneCnt > K) {
// System.out.println(Arrays.stream(binaryValue).mapToObj(String::valueOf).collect(Collectors.joining()) + ", " + oneCnt);
if (binaryValue[i] == 1) {
binaryValue[i] = 0;
binaryValue[i-1]++;
for (int j=i-1; binaryValue[j] == 2; j--) {
binaryValue[j] = 0;
binaryValue[j-1]++;
oneCnt--;
}
}
i--;
}
}
private static int getDiff() {
StringBuilder sb = new StringBuilder();
for (int e : binaryValue) {
sb.append(e);
}
return Integer.parseInt(sb.toString(), 2) - N;
}
}