baekjoon/String

백준 1036번 : 36진수 자바

Meluu_ 2024. 8. 28. 14:54

🧫 문제 분석

 

✔️ 출처

36진수 골드 1

 

📖 문제

 

 

주어진 입력 중 Z로 k개 만큼 바꾸고 모두 더한 값이 최댓값이 되게 구하고, 그 값을 36진수로 변환하여 출력

 

정말정말 많은 시간을 썼고, 다른 블로그의 앞부분만 슬쩍봐서 큰 수를 어떻게 다루는지 참고하면서 풀었다. 

결국에는 내가 생각한 아이디어와 로직이 맞았고, 큰 수를 다루는 BigInteger를 잘 안써서 떠오르지 못했던 것이다.. 

 

 

나의 경우 

0~9 는 배열 각 인덱스 0~9에 1:1로 사용

A~Z는 -'7'을 해주면 'A' - '7' = 10이 되므로

배열을 35까지 만든 후 

배열의 인덱스 0~9 A~Z까지 0~35로 사용하였다.

 

이 문제에서 중요한 것

Z로 바꾼 K개의 숫자가 무엇이냐다.

맨 앞자리 수를 Z로 만든다고 무조건 최댓값이 나오는 것이 아니다. 

 

예를 들어  이런 예제가 있다고 치자

3
Y2
1
1
1
답 : 100

여기서  Y의 자릿수가 제일 크기에 Y를 Z로 바꾸면 최댓값이 되지 않을까 싶지만

실제로 계산해보면 1을 Z로 만드는게 더 큰 값이 나온다. 

참고로 Y를 Z로 변환시 Z6이 된다. 

 

 

k개를 뽑을 때

변화 값이 가장 큰 수를 Z로 만들어야 하기에 

(35 - 수) * 35^자릿수

이렇게 해서 가장 큰 변화값을 가지는 수를 뽑아야한다.

 

int, long 형을 쓰면 안되는 이유는 문제에서 수의 길이가 50자리라고 했다. 

35^50은 이미 범위를 넘어선다. 

따라서 BigInteger을 사용하면 된다.

 

 

 

 


🔅 문제 풀이

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        HashMap<Integer, BigInteger> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();

        int n = Integer.parseInt(br.readLine());
        String[] word = new String[n];
        int[] arr = new int[36];

        int min = 100, max = 0;
        for (int i = 0; i < n; i++) {
            word[i] = br.readLine();

            min = Math.min(word[i].length(), min);
            max = Math.max(word[i].length(), max);

            for (int j = 0; j < word[i].length(); j++) {
                char number = word[i].charAt(j);

                BigInteger bInt = new BigInteger("0");
                // 최댓값을 구하기 위한 개수 구하기
                int power = word[i].length() - j;
                int key = number >= 'A' ? number - '7' : number - '0';

                BigInteger value = map.getOrDefault(key, bInt);
                bInt = BigInteger.valueOf(35);
                bInt = bInt.pow(power).multiply(BigInteger.valueOf(35 - key)).add(value);


                map.put(key, bInt);
            }
        }

		// 변화 값이 가장 큰 수 대로 숫자를 오름차 정렬
        List<Map.Entry<Integer, BigInteger>> list = new ArrayList<>(map.entrySet());
        Collections.sort(list, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));


        int k = Integer.parseInt(br.readLine());
        
        if (k != 0) {
            for (int i = 0; i < k; i++) {
                if (list.size() <= i) break;
                Map.Entry<Integer, BigInteger> entry = list.get(i);
                arr[entry.getKey()] = 35;
            }
        }

        int[] nums = new int[53];
        calculateNum(word, arr, nums);

        int idx = change36(nums);

        // 36진수로 바꿔서 출력
        for (int i = idx - 1; i >= 0; i--) {
            char cc = (char) (nums[i] >= 10 ? (nums[i] + '7') : (nums[i] + '0'));
            sb.append(cc);
        }

        bw.write(sb.toString().isEmpty() ? "0" : sb.toString());
        bw.flush();
        bw.close();
    }

    private static void calculateNum(String[] word, int[] arr, int[] nums) {
        // 각 자리수의 값 계산
        for (int i = 0; i < word.length; i++) {

            for (int j = 0; j < word[i].length(); j++) {
                char c = word[i].charAt(word[i].length() - j - 1);

                if (arr[getIndex(c)] != 0) nums[j] += 35;
                else nums[j] += getIndex(c);
            }
        }
    }

    // 36진수 계산
    private static int change36(int[] nums) {
        int idx = 0;
        for (int i = 0; i < nums.length - 1; i++) {
            if (nums[i] > 0) {
                if (nums[i] / 36 > 0) {
                    nums[i+1] += nums[i] / 36;
                    nums[i] %= 36;
                }
                idx = Math.max(idx, i+1); // 끝에 있는 수의 자릿수를 세기
            }
        }
        return idx;
    }

    private static int getIndex(char c) {
        return c >= 'A' ? c - '7' : c - '0';
    }

}

 

 

 

36진수 계산 메서드에서  

따로 idx 변수를 써서 36진수로 계산된 자릿수의 크기를 미리 카운팅 할 건데 

왜 idx++ 이 아니라 Math.max() 를 썼느냐

 아래의 예시를 보면 이해할 수 있다.

2
ZZZ000000000000ZZ
1
0
정답 : ZZZ00000000000100
오답 : 000100

 

 

기존의 자릿수에 값이 0이면 내가 짠 로직은 넘어가게 되있다. 

따라서 Math.max 함수를 써서 마지막으로 뜬 가장 큰 자릿수의 수의 인덱스를 세기 위함이다.

 

물론 마지막 큰 숫자의 자릿수만 알면 되므로 아래와 같이 써도 된다.

    // 36진수 계산
    private static int change36(int[] nums) {
        int idx = 0;
        for (int i = 0; i < nums.length - 1; i++) {
            if (nums[i] > 0) {
                if (nums[i] / 36 > 0) {
                    nums[i+1] += nums[i] / 36;
                    nums[i] %= 36;
                }
//                idx = Math.max(idx, i+1);
                    idx = i;
            }
        }
        return idx + 1;
    }

 

❗ 오답노트 / 필요한 지식

  1. 어려웠다. 어떻게 푸는지는 알겠는데 k개를 뽑을때 예외사항, 큰 수의 처리, Map의 정렬 이런 것 때문에 더 막힌듯 하다.