코딩테스트/BOJ

[BOJ] 18111 마인크래프트 - JAVA

5월._. 2022. 12. 16.
728x90

1. 문제

 

18111번: 마인크래프트

팀 레드시프트는 대회 준비를 하다가 지루해져서 샌드박스 게임인 ‘마인크래프트’를 켰다. 마인크래프트는 1 × 1 × 1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게

www.acmicpc.net

팀 레드시프트는 대회 준비를 하다가 지루해져서 샌드박스 게임인 ‘마인크래프트’를 켰다. 마인크래프트는 1 × 1 × 1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게 땅을 파거나 집을 지을 수 있는 게임이다.

목재를 충분히 모은 lvalue는 집을 짓기로 하였다. 하지만 고르지 않은 땅에는 집을 지을 수 없기 때문에 땅의 높이를 모두 동일하게 만드는 ‘땅 고르기’ 작업을 해야 한다.

lvalue는 세로 N, 가로 M 크기의 집터를 골랐다. 집터 맨 왼쪽 위의 좌표는 (0, 0)이다. 우리의 목적은 이 집터 내의 땅의 높이를 일정하게 바꾸는 것이다. 우리는 다음과 같은 두 종류의 작업을 할 수 있다.

좌표 (i, j)의 가장 위에 있는 블록을 제거하여 인벤토리에 넣는다.
인벤토리에서 블록 하나를 꺼내어 좌표 (i, j)의 가장 위에 있는 블록 위에 놓는다.
1번 작업은 2초가 걸리며, 2번 작업은 1초가 걸린다. 밤에는 무서운 몬스터들이 나오기 때문에 최대한 빨리 땅 고르기 작업을 마쳐야 한다. ‘땅 고르기’ 작업에 걸리는 최소 시간과 그 경우 땅의 높이를 출력하시오.

단, 집터 아래에 동굴 등 빈 공간은 존재하지 않으며, 집터 바깥에서 블록을 가져올 수 없다. 또한, 작업을 시작할 때 인벤토리에는 B개의 블록이 들어 있다. 땅의 높이는 256블록을 초과할 수 없으며, 음수가 될 수 없다.


2. 풀이

Main

static N 세로
static M 가로
static int B 인벤토리에 저장된 블록 개수
int[][] map 지도
int max 최대 땅 높이
int min 최저 땅 높이

1.  map을 입력받으면서 최고, 최저 땅높이를 저장한다. 이 때, max와 min의 초기값은 각각 0과 257이다. 문제에서 땅의 높이가 256블록을 초과할 수 없으며 음수가 될 수 없다고 했기 때문이다.

2.  max부터 min까지 level을 하나씩 줄여가면서 값을 체크한다. level의 초기값은 max다.

3.  2가 끝났다면 static으로 저장해둔 최소시간과 최대 땅높이를 출력한다.

   static int N, M, B;                  //세로,가로,인벤토리에 저장된 블록 개수
   static int[][] map;                  //지도
   static int TIME = Integer.MAX_VALUE; //최소 시간
   static int LEVEL;                    //최소 시간일 때의 최대 땅높이

   public static void main(String[] args) throws IOException {
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      StringTokenizer st = new StringTokenizer(in.readLine());

      //변수
      N = Integer.parseInt(st.nextToken()); //세로
      M = Integer.parseInt(st.nextToken()); //가로
      B = Integer.parseInt(st.nextToken()); //인벤토리에 저장된 블록 개수
      map = new int[N][M];                  //지도
      int max = 0;                          //최고 땅높이
      int min = 257;                        //최저 땅높이

      //입력
      for (int i = 0; i < N; i++) {
         st = new StringTokenizer(in.readLine());
         for (int j = 0; j < M; j++) {
            map[i][j] = Integer.parseInt(st.nextToken());
            max = Math.max(max,map[i][j]);
            min = Math.min(min,map[i][j]);
         }
      }

      //반복 : max -> min까지
      int level = max;
      while (level >= min) {
         checkMap(level--);
      }

      //출력
      System.out.println(TIME + " "+LEVEL);
   }

 

checkMap

static int TIME 최소 시간
static int LEVEL 최소 시간일 때의 최고 땅높이
int level 기준 땅 높이
int calc level값에 따른 블록 여유분. B부터 시작한다.
int time level값에 따른 평평하게 만드는 데 걸린 시간.

1.  calc에 B, time에 0을 넣고 시작한다.

2.  map을 하나하나 level과 비교하면서 높이차 diff를 구한다.

2-1.  만약 map[i][j]가 level보다 크면 시간에 diff*2만큼 더한다. (블록을 빼는 건 시간이 두 배 걸린다.) 여유분에는 diff만큼 더한다.

2-2.  만약 map[i][j]가 level보다 같거나 작다면 블록을 더해야 한다. 시간에 diff만큼 더하고, 여유분에서 diff만큼 뺀다.

3.  2가 마무리된 후 여유분이 0보다 크거나 같다면 level만큼 땅을 평평하게 만드는 데 성공한 것이다. 여유분이 음수라면 끝낸다.

4.  현재시간이 static TIME보다 작다면 TIME, LEVEL을 전부 현재값으로 변경한다.

5.  현재시간이 static TIME과 같고 현재 땅높이가 LEVEL보다 크다면 LEVEL을 변경한다.

   //지도 체크. 
   private static void checkMap(int level) {
      int calc = B;//여유분. B부터 시작한다.
      int time = 0;//걸린 시간.

      for (int i = 0; i < N; i++) {
         for (int j = 0; j < M; j++) {
            //미리 level과 현재 위치의 높이차를 구해둔다.
            int diff = Math.abs(map[i][j]-level);
            if (map[i][j] > level) {
               //level보다 크면 시간을 diff*2만큼 더하고,
               //여유분을 diff만큼 더한다.            
               time += 2*diff;
               calc += diff;
            } else {
               //level보다 같거나 작다면 시간에 diff만큼 더하고,
               //diff만큼 여유분에서 뺀다.            
               time += diff;
               calc -= diff;
            }
         }
      }

      //만약 여유분이 0보다 크다면 
      if (calc >= 0) {
         //현재 시간이 static TIME보다 작은지 확인한다.      
         if(time < TIME) {
            //작다면 TIME, LEVEL을 현재값으로 전부 교체한다.         
            TIME = time;
            LEVEL = level;
         }
         else if (time == TIME && level > LEVEL) {
            //시간이 static TIME과 같고 level이 LEVEL보다 크다면 교체한다.         
            LEVEL = level;
         }
      }

   }

3. 결과

최소 ~ 최대 범위를 정확히 지정하지 않아서 시간초과도 나고 아예 틀려보기도 했다. 

B + 셀의 높이를 전부 더해서 평균값을 내고 그 값 기준으로 반올림하려고 했는데 그렇게하면 고려할 사항이 너무 많아서 그만뒀다.

'코딩테스트 > BOJ' 카테고리의 다른 글

[BOJ] 13398 연속합2 - JAVA  (0) 2022.12.16
[BOJ] 1922 네트워크 연결 - JAVA  (0) 2022.12.16
[BOJ] 2004 조합 0의 개수 - JAVA  (0) 2022.07.29
[BOJ] 1965 상자넣기 - JAVA  (0) 2022.07.28
[BOJ] 1735 분수 합 - JAVA  (0) 2022.07.27

댓글