백준15686 : 치킨 배달


문제

NxN 배열 빈칸(0), 집(1), 치킨집(2) 이 있을 때, 이 중 치킨집을 M개만 남겼을 때, 최대가 되는 치킨거리를 구하는 문제. 치킨거리는 각 집에서 가장 가까운 치킨집의 거리를 모두 더한 값이다.

https://www.acmicpc.net/problem/15686


접근방법

  • 치킨 리스트 중 M개를 선택한다. (combination 이용)

  • 각 집 당, 고른 M개의 치킨집과의 거리를 비교해 가장 짧은 거리를 구하고, 이들을 모두 더한 치킨거리를 구한다.

  • 최소의 치킨거리를 구한다.

  1. 치킨집 M개 고르기

    • 치킨집을 ArrayList에 저장하고, combination 함수를 이용해 고를 치킨집의 인덱스를 구한다.

  2. 각 집의 치킨거리 구하기

    • 모든 집과 모든 치킨집을 비교해 최솟값을 구하고, 각 집의 최솟값을 더해 치킨거리를 구한다.


소스코드

import java.util.*;
import java.io.*;

public class Main{

   static final int MAX = Integer.MAX_VALUE;
   static int N;
   static int M;
   static int map[][];
   static int min_dist;
   static ArrayList<Pos> home_list;
   static ArrayList<Pos> chicken_list;

   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x = x; this.y = y;}
  }

   public static void combination(int arr[], int index, int n, int r, int target) {
       if(r == 0) {
           int cur_chick_dist = chickDist(arr);
           if(cur_chick_dist < min_dist)
               min_dist = cur_chick_dist;
      }else if(target == n) {
           return;
      }else {
           arr[index] = target;
           combination(arr, index+1, n, r-1, target+1);
           combination(arr, index, n, r, target+1);
      }
  }

   static int chickDist(int[] arr) {
       int sum = 0;   // M개 치킨집을 고른 맵에서의 치킨거리

       int[][] map_tmp = new int[N][N];
       for(int i=0; i<N; i++)
           for(int j=0; j<N; j++) {
               map_tmp[i][j] = map[i][j];
               if(map_tmp[i][j] == 2)
                   map_tmp[i][j] = 0; // 치킨집 초기화(아무 치킨집도 선택되지 않은 상태)
          }

       // 치킨집 선택
       ArrayList<Pos> tmp_chickList = new ArrayList();
       for(int i=0; i<arr.length; i++) {
           Pos chick_pos = chicken_list.get(arr[i]);
           map_tmp[chick_pos.x][chick_pos.y] = 2;
           tmp_chickList.add(chick_pos);
      }

       // 각 사람당 가장 가까운 치킨집과의 거리 계산 후 합산
       for(int i=0; i<home_list.size(); i++) {
           int person_min_dist = MAX;
           for(int j=0; j<M; j++) {
               int cur_dist = 0;
               cur_dist = Math.abs(home_list.get(i).x - tmp_chickList.get(j).x)
                       + Math.abs(home_list.get(i).y - tmp_chickList.get(j).y);
               if(cur_dist < person_min_dist)
                   person_min_dist = cur_dist;
          }
           sum += person_min_dist;
      }

       return sum;
  }

   public static void main(String[] args) throws IOException {

       BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

       String input[] = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       M = Integer.parseInt(input[1]);
       map = new int[N][N];
       home_list = new ArrayList();
       chicken_list = new ArrayList();

       for(int i=0; i<N; i++) {
           String line[] = in.readLine().split(" ");
           for(int j=0; j<N; j++) {
               map[i][j] = Integer.parseInt(line[j]);
               if(map[i][j] == 1)
                   home_list.add(new Pos(i, j));
               else if(map[i][j] == 2)
                   chicken_list.add(new Pos(i,j));
          }
      }

       min_dist = MAX;
       combination(new int[M], 0, chicken_list.size(), M, 0);

       System.out.println(min_dist);


  }

}


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준16234 : 인구 이동  (0) 2018.11.21
백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16

백준16234 : 인구 이동

문제

2차원 배열에 국가별 인구 수가 주어져있을 때 인접한 국가의 인구수가 주어진 범위 내에 있으면 국경을 열어 인구이동을 하여 1/N인구로 만드는 작업을 반복할 때, 인구이동이 몇 번 일어나는 지 알아내는 문제

https://www.acmicpc.net/problem/16234



접근방법

  • BFS를 이용해 인접한 영역을 확인하고 인구이동을 한다.

  • 인구이동을 한 국가는 booelan moved[][] 를 이용해 표시한다.

  • 한 번 전체 국가가 인구이동을 한 뒤에는 이동 수를 증가시키고 moved[][]를 초기화 해준다.


  1. BFS를 이용해 인접한 영역 확인하기

    • 2개의 boolean 2차원 배열을 사용해주었다.

      1) BFS 체크용 boolean 배열 check[][] 시간이 초과되지 않도록 이미 방문한 배열 체크

      2) 인구이동한 국가 확인용 boolean 배열 moved[][]

    • 인접한 배열을 큐에 넣을지 결정하기 위해서는 다음의 네가지를 확인해주어야 한다.

      1) 배열의 범위를 벗어났는가

      • 이 경우는 애초에 배열을 2칸씩 크게 만들어서, 해당 원소의 값이 0인지 여부만 확인해주면 된다.

      2) 아직 인구이동이 되지 않은 지역인가

      • moved 배열 확인

      3) BFS가 이미 방문한 원소인가

      • check 배열 확인

      4) 인구차이가 범위 내에 있는가

    • 위 네가지를 확인한 뒤에 큐에 넣어준다.


    인구이동이 일어난 국가들의 인구 수를 변경해주기 위해, 큐에서 빼낸 좌표들은 다시 새로운 큐(queue_tmp)에 넣어준다.

    • 해당 큐를 이용하여 인구 수를 설정해주고, moved를 함께 체크한다.

    • 인접값을 확인하기 전부터 미리 하나는 큐에 들어가므로, 큐의 개수가 1개 초과일 경우가 인구이동이 일어난 경우이다.




소스코드

import java.util.*;
import java.io.*;

public class Main{

   static int[] dx = {1, -1, 0, 0};
   static int[] dy = {0, 0, 1, -1};

   static int N;
   static int minGap;
   static int maxGap;
   static int[][] map;
   static boolean[][] moved;
   static boolean isQuit;

   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x= x; this.y = y;}
  }

   static void BFS(int x, int y){
       boolean check[][] = new boolean[N+2][N+2];
       Queue<Pos> queue = new LinkedList();
       Queue<Pos> queue_tmp = new LinkedList();
       queue.offer(new Pos(x, y));

       int sum = 0;
       while(!queue.isEmpty()) {
           Pos cur_pos = queue.poll();
           check[cur_pos.x][cur_pos.y] = true;
           sum += map[cur_pos.x][cur_pos.y];
           queue_tmp.offer(new Pos(cur_pos.x, cur_pos.y));

           for (int i = 0; i < 4; i++) {
               Pos next_pos = new Pos(cur_pos.x + dx[i], cur_pos.y + dy[i]);
               int gap = Math.abs(map[cur_pos.x][cur_pos.y] - map[next_pos.x][next_pos.y]);
               if( map[next_pos.x][next_pos.y] != 0        // 경계가 아니면서
                       && !moved[next_pos.x][next_pos.y]   // 아직 인구이동이 되지 않은 지역이면서
                       && !check[next_pos.x][next_pos.y]   // <<BFS방문체크>>
                       && gap >= minGap && gap <= maxGap){ // 인구 차이가 범위 내인 경우
                   queue.offer(next_pos);                  // 큐에 넣어준다.
                   check[next_pos.x][next_pos.y] = true;
              }
          }
      }

       // 인구이동이 일어난 경우
       if(queue_tmp.size() != 1){
           isQuit = false;
           int value = sum / queue_tmp.size();
           while(!queue_tmp.isEmpty()){
               Pos pos = queue_tmp.poll();
               map[pos.x][pos.y] = value;
               moved[pos.x][pos.y] = true;
          }
      }

  }


   public static void main(String[] args) throws IOException{
       BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
       String[] input = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       minGap = Integer.parseInt(input[1]);
       maxGap = Integer.parseInt(input[2]);

       map = new int[N+2][N+2];
       moved = new boolean[N+2][N+2];
       for(int i=1; i<=N; i++){
           String line[] = in.readLine().split(" ");
           for(int j=1; j<=N; j++)
               map[i][j] = Integer.parseInt(line[j-1]);
      }

       int result = 0;
       while(true){
           isQuit = true;
           for(int i=1; i<=N ;i++)
               for (int j = 1; j <= N; j++)
                   if (!moved[i][j])
                       BFS(i, j);

           if(!isQuit){
               ++result;
               // 인구이동여부 초기화
               for(int i=1; i<=N; i++)
                   for(int j=1; j<=N; j++)
                       moved[i][j] = false;
          } else{ // 인구이동 하나도 없을 시 종료
               break;
          }
      }

       System.out.println(result);
  }
}


'알고리즘' 카테고리의 다른 글

[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준15686 : 치킨 배달  (0) 2018.11.23
백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27

백준15684 : 사다리 조작

문제

N개의 세로선과 H개의 가로선이 있을 때 i번째 세로선을 타고 다시 i번째로 끝나기 위해 추가해야 될 가로선의 개수를 구하는 문제

https://www.acmicpc.net/problem/15684


접근방법

  • 3번까지만 사다리를 놓는다는 것을 보고 1번, 2번, 3번 사다리를 일일이 놓아본 다음 다시 본인의 세로선에서 끝나는지를 확인하도록 하였다.

  • 기본로직

    // i-i 연결 여부 확인
    if(search(map) == false){ // 초기상태 확인
       ++result;
       isTrue = combi_1(); // 가로선 1개 추가 후 확인
       if(isTrue == false){
           ++result;
      isTrue = combi_2(); // 가로선 2개 추가 후 확인
           if(isTrue == false){
               ++result;
               isTrue = combi_3(); // 가로선 3개 추가 후 확인
               if(isTrue == false)
                   result = -1;
          }
      }
    }
    System.out.println(result);


  1. i번째 세로선이 사다리를 타고 도달하는 세로선 확인하는 함수 search()

    • 먼저 입력을 받을 때 가로는 N-1개, 세로는 H개의 배열로 받아지는 것을 알 수 있다. 즉, map[i][j] 는 i번째 가로선과 j와 j+1번째 세로선을 잇는 선이 된다.

    • map[H][N-1] 배열에서 i번째 세로선에서 시작해 사다리를 타고 이동할 곳을 알기 위해서는 map[i][j-1]map[i][j] 을 확인해야 한다. 즉, 첫번째경우가 true라면 왼쪽으로 이동하는 것이고, 두번재 경우가 true라면 오른쪽으로 이동하는 경우이다.

    • 여기서 첫번째 세로선과 마지막 세로선은 한방향으로만 이동할 수 있기 때문에 예외처리를 해주어야 한다.

    • map[i][j-1], map[i][j] 에 따라서 세로선 인덱스가 바뀌기 때문에, 마지막에 처음 시작한 인덱스와 비교해주기 위해 n_tmp 를 사용하였다.

    • 소스코드는 아래와 같다.

      // i-i 연결여부 확인
      static boolean search(boolean[][] map){

         boolean result = true;
         for(int n=1; n<=N; n++){
             int n_tmp = n;
             for(int h=1; h<=H; h++) {
                 // 왼쪽으로 이동 (1은 확인x)
                 if (n > 1 && map[h][n - 1] == true) {
                     --n;
                }// 오른쪽으로 이동 (N은 확인x)
                 else if (n < N && map[h][n] == true){
                     ++n;
                }
            }
             if(n != n_tmp){
                 result = false;
                 break;
            }
             n = n_tmp;
        }

         return result;
      }


  1. 가로선을 하나 추가하는 경우는 이차원 배열을 훑으면서 하나씩 가로선을 더해주면 된다.

    • 소스코드

      static void combi_1(){
        boolean map_tmp[][] = new boolean[H+1][N];
         for(int i=1; i<=H; i++)
             for(int j=1; j<=N-1; j++)
                 map_tmp[i][j] = map[i][j];

         for(int i=0; i<pos_set.length; i++){
             if(pos_set[i] != null){
                 map_tmp[pos_set[i].x][pos_set[i].y] = true;
                 if(search(map_tmp)){
                     isTrue = true;
                     break;
                }// 바뀐 맵 원래대로 돌려놓기
                 map_tmp[pos_set[i].x][pos_set[i].y] = false;
            }
        }
      }


  1. 가로선을 2개, 3개 추가하는 경우는 combination함수를 사용하였다.

    • (x, y)의 위치를 조합으로 꺼내야하기 때문에, Pos 객체를 이용해 가로선이 없는 위치를 배열에 넣어주었다.


소스코드

import java.io.*;
import java.lang.reflect.Array;
import java.util.ArrayList;

public class Main{

   static int N;
   static int M;
   static int H;
   static boolean isTrue;
   static boolean map[][];
   static Pos[] pos_set = new Pos[H*(N-1)];


   static class Pos{
       int x; int y;
       public Pos(int x, int y) {this.x = x; this.y = y;}
  }

   // i-i 연결여부 확인
   static boolean search(boolean[][] map){

       boolean result = true;
       for(int n=1; n<=N; n++){
           int n_tmp = n;
           for(int h=1; h<=H; h++) {
               // 왼쪽으로 이동 (1은 확인x)
               if (n > 1 && map[h][n - 1] == true) {
                   --n;
              }// 오른쪽으로 이동 (N은 확인x)
               else if (n < N && map[h][n] == true){
                   ++n;
              }
          }
           if(n != n_tmp){
               result = false;
               break;
          }
           n = n_tmp;
      }

       return result;
  }

   static void combi_1(){
      boolean map_tmp[][] = new boolean[H+1][N];
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
               map_tmp[i][j] = map[i][j];

       for(int i=0; i<pos_set.length; i++){
           if(pos_set[i] != null){
               map_tmp[pos_set[i].x][pos_set[i].y] = true;
               if(search(map_tmp)){
                   isTrue = true;
                   break;
              }// 바뀐 맵 원래대로 돌려놓기
               map_tmp[pos_set[i].x][pos_set[i].y] = false;
          }
      }
  }

   static void combi_2(int arr[]){
       boolean map_tmp[][] = new boolean[H+1][N];
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
               map_tmp[i][j] = map[i][j];

       map_tmp[pos_set[arr[0]].x][pos_set[arr[0]].y] = true;
       map_tmp[pos_set[arr[1]].x][pos_set[arr[1]].y] = true;

       if(search(map_tmp))
           isTrue = true;
  }

   static void combi_3(int arr[]){
       boolean map_tmp[][] = new boolean[H+1][N];
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
               map_tmp[i][j] = map[i][j];

       map_tmp[pos_set[arr[0]].x][pos_set[arr[0]].y] = true;
       map_tmp[pos_set[arr[1]].x][pos_set[arr[1]].y] = true;
       map_tmp[pos_set[arr[2]].x][pos_set[arr[2]].y] = true;

       if(search(map_tmp))
           isTrue = true;
  }

   static void combination(int arr[], int index, int n, int r, int target){
       if(arr.length == 1) {
           combi_1();
      }else {
           if (r == 0) {
               if (arr.length == 2) { // 두 개 고르는 경우
                   combi_2(arr);
              } else if (arr.length == 3) { // 세 개 고르는 경우
                   combi_3(arr);
              }
          } else if (target == n) {
               return;
          } else {
               arr[index] = target;
               combination(arr, index + 1, n, r - 1, target + 1);
               combination(arr, index, n, r, target + 1);
          }
      }
  }


   public static void main(String[] args) throws IOException {

       BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

       String input[] = in.readLine().split(" ");
       N = Integer.parseInt(input[0]);
       M = Integer.parseInt(input[1]);
       H = Integer.parseInt(input[2]);
       map= new boolean[H+1][N]; // 한 칸씩 크게 맵을 만들어 줌(H x N-1)

       for(int i=0; i<M; i++){
           String line[] = in.readLine().split(" ");
           map[Integer.parseInt(line[0])][Integer.parseInt(line[1])] = true;
      }

       isTrue = false;
       int result = 0;

       //pos_set = new Pos[H*(N-1)];
       ArrayList<Pos> pos_list = new ArrayList();
       int p_index = 0;
       for(int i=1; i<=H; i++)
           for(int j=1; j<=N-1; j++)
              if(map[i][j] == false)
                  pos_list.add(new Pos(i, j));
       pos_set = pos_list.toArray(new Pos[p_index]);

       // i-i 연결 여부 확인
       if(search(map) == false){ // 초기상태 확인
           ++result;
           int arr_1[] = new int[1];
           combination(arr_1,0, pos_set.length, 1, 0);
           if(isTrue == false){ // 가로선 1개 추가 후 확인
               ++result;
               int arr_2[] = new int[2]; // 가로선 2개 추가 후 확인
               combination(arr_2,0, pos_set.length, 2, 0);
               if(isTrue == false){
                   ++result;
                   int arr_3[] = new int[3];
                   combination(arr_3,0, pos_set.length, 3, 0);
                   if(isTrue == false)
                       result = -1;
              }
          }
      }

       System.out.println(result);

  }
}


- 정답은 맞았지만 시간 상 수정이 필요하다.

'알고리즘' 카테고리의 다른 글

백준15686 : 치킨 배달  (0) 2018.11.23
백준16234 : 인구 이동  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20

백준14889 : 스타트와 링크

문제

https://www.acmicpc.net/problem/14889

i멤버와 j멤버가 같은 팀에 있을 때에 갖는 능력치 배열 Sij가 주어졌을 때, N명의 멤버를 스타트팀과 링크팀 절반으로 나누었을 때 각 팀 능력치 차의 최솟값을 구하는 문제


접근방법

  1. N명 중 N/2명을 고르는 combination 함수를 이용한다.

    • N명 중 N/2명을 골라 스타트팀에 넣으면 선택되지 않은 멤버는 링크팀에 속하게 된다.

    • nCr의 조합 경우의 수를 출력하는 것은 아래의 함수를 이용하였다.

    static void combination(int[] arr, int index, int n, int r, int target){
       if(r == 0){
           /* 선택할 r개를 고를 때 마다 실행되는 부분 */
      }else if(target == n){
           return;
      }else {
           arr[index] = target;
           combination(arr, index+1, n, r-1, target+1);
           combination(arr, index, n, r, target +1);
      }
    }
    • if(r == 0) 부분이 실행되면 배열 arr에 선택한 r개가 담긴다. 이 함수에 담긴 인덱스들은 start[] 배열에 저장하고, 여기에 담기지 않은 인덱스들은 link[] 배열에 저장한다.

      • 구분해주기 위해 boolean check[] 배열을 이용하였다.

    • 이 때, 조합을 절반만 구하면 나머지 절반은 자동으로 다른 팀으로 정해지기 때문에 nCr을 구하는 함수를 절반만 실행하면 된다.

      예를 들어 4C2는 (1,2) (1,3) (1,4) (2,3) (2,4) (3,4)가 되는데, (1,2) 를 추출하면 (3,4)는 자동으로 다른 팀이 되므로, (1,2) (1,3) (1,4) 만 구하면된다. 즉, nCr/2 만큼만 조합 경우의 수를 계산하는 함수를 실행해주면 된다.

  1. 각 팀의 능력치를 계산한다.

    • 예를 들어 (2, 3, 5) 와 (1, 4, 6) 으로 팀을 나누었을 때, 스타트팀의 능력치는 S(2,3) + S(3,2) + S(2,5) + S(5,2) + S(3,5) + S(5,3)이 된다.

    • 이를 계산해주기 위해 for문을 사용하였다.

      for(int i=0; i<N/2; i++)
         for(j=i+1; j<N/2; j++){
             start_stat += S[start[i]][start[j]] + S[start[j]][start[i]];
             link_stat += S[link[i]][link[j]] + S[link[j]][link[i]];
        }

  1. 계산한 능력치의 최소차를 구한다.


소스코드

import java.io.*;
import java.util.*;

public class Main{

   static int N;
   static int S[][];
   static int min_statDiff;
   static int count;

   // nCr 개수
   static int combb(int n, int r) {
       if(n == r || r == 0)
           return 1;
       else
           return combb(n - 1, r - 1) + combb(n - 1, r);
  }

   static void combination(int[] arr, int index, int n, int r, int target){
       if(r == 0){
           if(count > 0){  // nCr / 2 만큼만 조합 경우의 수 구하기
               int cur_statDiff = calcStat(arr);
               if(min_statDiff == -1 || cur_statDiff < min_statDiff)
                   min_statDiff = cur_statDiff;
               --count;
          }
      }else if(target == n){
           return;
      }else {
           arr[index] = target;
           combination(arr, index+1, n, r-1, target+1);
           combination(arr, index, n, r, target +1);
      }
  }

   static int calcStat(int[] start){
       boolean check[] = new boolean[N];
       int[] link = new int[N/2];

       for(int i=0; i<N/2; i++)
           check[start[i]] = true;
       int j = 0;
       for(int i=0; i<N; i++)
           if(check[i] == false)
               link[j++] = i;

       int start_stat = 0;
       int link_stat = 0;

       for(int i=0; i<N/2; i++)
           for(j=i+1; j<N/2; j++){
               start_stat += S[start[i]][start[j]] + S[start[j]][start[i]];
               link_stat += S[link[i]][link[j]] + S[link[j]][link[i]];
          }
       return Math.abs(start_stat - link_stat);
  }


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

       N = Integer.parseInt(in.readLine());
       S = new int[N][N];
       int combi_set[] = new int[N/2];

       for(int i=0; i<N; i++){
           String line[] = in.readLine().split(" ");
           for(int j=0; j<N; j++)
               S[i][j] = Integer.parseInt(line[j]);
      }

       count = combb(N, N/2)/2;
       min_statDiff = -1;
       combination(combi_set, 0, N, N/2, 0 );

       System.out.println(min_statDiff);

  }
}

'알고리즘' 카테고리의 다른 글

백준16234 : 인구 이동  (0) 2018.11.21
백준15684 : 사다리 조작  (0) 2018.11.21
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20
[JAVA] 백준 14503 : 로봇 청소기  (0) 2018.09.18

[JAVA] 백준 3055 : 탈출


문제

시간이 지날 수록 물이 차오르는 상황에서 고슴도치 집에서 출발한 고슴도치가 친구인 비버 집에 얼마만에 도착할 수 있는가를 구하는 문제. R행 C열로 이뤄진 지도에는 빈칸(.), 물(*), 돌(X), 비버집(D), 고슴도치집(S)이 주어져있다. 매 분마다 고슴도치는 현재 있는 칸과 인접한 네 칸 중 하나로 이동할 수 있고, 물도 매 분마다 비어있는 칸으로 확장한다. 물이 있는 칸과 인접해있는 비어있는 칸은 물이 차게 된다. 물과 고슴도치는 돌을 통과할 수 없다. 또, 고슴도치는 물로 차있는 구역으로 이동할 수 없고, 물도 비버의 소굴로 이동할 수 없다. 이러한 지도가 주어졌을 때, 고슴도치가 안전하게 비버의 굴로 이동하기 위해 필요한 최소 시간을 구하는 문제.



접근방법

  1. 맵의 입력을 숫자로 변환하여 받는다.

    • 빈칸(-2), 물(-1), 돌(2) 고슴도치집(0), 비버집(-100)으로 설정하였다. → 처음 큐에 들어가면서 1씩 증가해야 하므로 고슴도치 집을 0으로 설정하였고, 이와 구분해주기 위해 다른 값들은 위와 같이 설정하였다.

  2. 입력을 받으면서 물을 큐에 넣고, 그 뒤에 고슴도치의 위치를 큐에 넣으면서 BFS를 수행한다. 먼저 물과 인접한 빈칸이 물로 바뀌고, 고슴도치는 나머지 빈칸을 이동해 비버의 집까지의 이동거리를 체크한다.



소스코드

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main{

   static int[] dr = {1, -1, 0, 0};
   static int[] dc = {0, 0, 1, -1};

   static class Pos{
       int r;
       int c;
       public Pos(int r, int c) {this.r = r; this.c = c;}
  }

   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int N = sc.nextInt();
       int M = sc.nextInt();

       Queue<Pos> queue = new LinkedList<>();
       int map[][] = new int[N+2][M+2];
       boolean check_doch[][] = new boolean[N+2][M+2];
       boolean check_rain[][] = new boolean[N+2][M+2];

       // 돌(테두리) 세우기
       for(int i=0; i<N+2; i++)
           for(int j=0; j<M+2; j++)
               map[i][j] = 2;  // 1-벽(돌)

       Pos dochi = new Pos(0,0);
       Pos bibber = new Pos(0,0);
       for(int i=1; i<=N; i++){
           String line = sc.next();
           for(int j=1; j<=M; j++){
               char input = line.charAt(j-1);
               switch (input){
                   case '*':   // 물
                       map[i][j] = -1;
                       queue.offer(new Pos(i, j));
                       check_rain[i][j] = true;
                       break;
                   case 'S':   // 고슴도치 집
                       map[i][j] = 0;
                       dochi = new Pos(i, j);
                       break;
                   case '.':   // 빈칸
                       map[i][j] = -2;
                       break;
                   case 'D':   // 비버 집
                       map[i][j] = -100;
                       bibber = new Pos(i, j);
                       break;
                   case 'X':   // 돌
                       map[i][j] = 2;
                       break;
              }
          }
      }
       queue.offer(dochi);
       check_doch[dochi.r][dochi.c] = true;

       First_Loop:
       while(!queue.isEmpty()) {
           Pos cur_pos = queue.poll();
           for (int i = 0; i < 4; i++) {
               Pos next_pos = new Pos(cur_pos.r + dr[i], cur_pos.c + dc[i]);

               // 현재 칸이 빗물일 경우 -> 빗물 증가 (방문여부 확인할 필요x)
               if (map[cur_pos.r][cur_pos.c] == -1) {
                   // 주변 칸이 빈칸(-2)일 경우 큐에 넣는다.
                   if (map[next_pos.r][next_pos.c] == -2) {
                       map[next_pos.r][next_pos.c] = -1;
                       check_rain[next_pos.r][next_pos.c] = true;
                       queue.offer(next_pos);
                  }
              } // 현재 칸이 빈칸일 경우 -> 고슴도치 이동거리 증가
               else {
                   // 주변 칸이 방문하지 않았으면서
                   if (!check_doch[next_pos.r][next_pos.c]) {
                       // 비버의 집(-100)일 경우 while문 탈출
                       if (map[next_pos.r][next_pos.c] == -100){
                           map[next_pos.r][next_pos.c] = map[cur_pos.r][cur_pos.c] + 1;
                           break First_Loop;
                      }// 빈칸(-2)일 경우 계속해서 이동
                       else if(map[next_pos.r][next_pos.c] == -2){
                           map[next_pos.r][next_pos.c] = map[cur_pos.r][cur_pos.c] + 1;
                           check_doch[next_pos.r][next_pos.c] = true;
                           queue.offer(next_pos);
                      }
                  }
              }
          }
      }

       if(map[bibber.r][bibber.c] == -100)
           System.out.println("KAKTUS");
       else
           System.out.println(map[bibber.r][bibber.c]);
  }
}


'알고리즘' 카테고리의 다른 글

백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20
[JAVA] 백준 14503 : 로봇 청소기  (0) 2018.09.18
[JAVA] 백준 3190 : 뱀  (1) 2018.09.18

[JAVA] 백준 11559 : 뿌요뿌요

문제

뿌요뿌요 룰대로 필드가 주어졌을 때 콤보가 몇 번인지 구하는 문제

- 필드에 여러 가지 색깔의 뿌요를 놓는다. 뿌요는 중력의 영향을 받아 아래에 바닥이나 다른 뿌요가 나올 때까지 아래로 떨어진다.

- 뿌요를 놓고 난 후, 같은 색 뿌요가 4개 이상 상하좌우로 연결되어 있으면 연결된 같은 색 뿌요들이 한꺼번에 없어진다.

- 뿌요들이 없어지고 나서 위에 다른 뿌요들이 있다면, 역시 중력의 영향을 받아 차례대로 아래로 떨어지게 된다.

- 아래로 떨어지고 나서 다시 같은 색의 뿌요들이 4개 이상 모이게 되면 또 터지게 되는데, 터진 후 뿌요들이 내려오고 다시 터짐을 반복할 때마다 1연쇄씩 늘어난다.

- 터질 수 있는 뿌요가 여러 그룹이 있다면 동시에 터져야 하고 여러 그룹이 터지더라도 한번의 연쇄가 추가된다.


접근방법

  1. 4개 이상 붙어있는 것들을 체크한다.

  2. 1에 해당하는 뿌요들을 한 번에 없애고 위에 있는 뿌요들을 내려준다.

  3. 위 단계를 4개 이상 붙어있는 것이 없을 때까지 반복한다.




    1. "4개 이상 붙어있는 것들을 체크한다."

    • BFS를 사용해 붙어있는 것들을 체크한다.

    • 이 때, 큐를 하나만 사용하면 큐에 있는 것들이 없어지면서(poll) 인접한 배열 원소들을 큐에 넣는 방식이기 때문에, 인접한 원소를 모두 가지고 있을 큐(count_queue)를 하나 더 만든다.

    • 즉, 큐에서 poll() 한 원소를 새로운 큐에 offer() 한다.

    • 새로운 큐의 개수가 4개 이상인지 체크한다.

    private static int count(char alpha){
       Pos cur_pos;
       while(!queue.isEmpty()){
           cur_pos = queue.poll();
           count_queue.offer(cur_pos);

           for(int i=0; i<4; i++){
               Pos next_pos = new Pos(cur_pos.r + dr[i], cur_pos.c + dc[i]);
               // 동일한 색이면서 아직 방문하지 않았으면 큐에 삽입
               if(map[next_pos.r][next_pos.c] == alpha && !check[next_pos.r][next_pos.c]){
                   check[next_pos.r][next_pos.c] = true;
                   queue.offer(next_pos);
              }
          }
      }

       // count_queue : 붙어있는 뿌요들의 모임
       return count_queue.size();
    }

○ 새로운 큐의 개수가 4개 이상이면 해당 배열 원소를 'd'로 바꿔준다.


  1. 1에 해당하는 뿌요들을 한 번에 없애고 위에 있는 뿌요들을 내려준다.

    • 없어질 뿌요 위에 있는 원소들을 하나하나 밑으로 내려준다.

    • 처음에 배열을 (N+2) x (N+2) 로 설정해놓고, 테두리에 '.'로 설정해주었으므로 배열 범위 체크를 따로 해주지 않아도 된다.

    private static void deletePuyo(Pos pos){
       for(int i=pos.r; i>=1; i--)
           map[i][pos.c] = map[i-1][pos.c];
    }

  1. 위 단계를 4개 이상 붙어있는 것이 없을 때까지 반복한다.

    • 1에서 4개 이상의 뿌요가 붙어있다면 isQuit 변수를 true로 바꿔주고, combo를 증가시키기 전 해당 변수를 확인해 false라면 combo를 증가시키지 않고 while문을 빠져나간다.



소스코드

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main{

   static int dr[] = {1, -1, 0, 0};
   static int dc[] = {0, 0, 1, -1};

   static int N = 12;
   static int M = 6;
   static char map[][];
   static boolean check[][];
   static Queue<Pos> queue;
   static Queue<Pos> count_queue;

   static class Pos{
       int r;
       int c;
       public Pos(int r, int c) {this.r = r; this.c = c;}
  }

   private static int count(char alpha){
       Pos cur_pos;
       while(!queue.isEmpty()){
           cur_pos = queue.poll();
           count_queue.offer(cur_pos);

           for(int i=0; i<4; i++){
               Pos next_pos = new Pos(cur_pos.r + dr[i], cur_pos.c + dc[i]);
               // 동일한 색이면서 아직 방문하지 않았으면 큐에 삽입
               if(map[next_pos.r][next_pos.c] == alpha && !check[next_pos.r][next_pos.c]){
                   check[next_pos.r][next_pos.c] = true;
                   queue.offer(next_pos);
              }
          }
      }

       // count_queue : 붙어있는 뿌요들의 모임
       return count_queue.size();
  }

   private static void deletePuyo(Pos pos){
       for(int i=pos.r; i>=1; i--)
           map[i][pos.c] = map[i-1][pos.c];
  }


   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int combo = 0;
       N = 12;
       M = 6;

       map = new char[N+2][M+2];
       check = new boolean[N+2][M+2];
       queue = new LinkedList<>();
       count_queue = new LinkedList<>();

       // 초기화 (테두리 설정)
       for(int i=0; i<N+2; i++)
           for(int j=0; j<M+2; j++)
               map[i][j] = '.';

       // 맵 설정
       for(int i=1; i<=N; i++){
           String line = sc.nextLine();
           for(int j=1; j<=M; j++)
               map[i][j] = line.charAt(j-1);
      }

       boolean isQuit = false;
       while(true){
           isQuit = true;
           for(int i=1; i<=N; i++){
               for(int j=1; j<=M; j++){
                   // 배열이 값이 알파벳이면서 아직 체크하지 않았다면 큐에 넣는다.
                   if(Character.isAlphabetic(map[i][j]) && !check[i][j]){
                       check[i][j] = true;
                       queue.offer(new Pos(i, j));

                       // 붙어있는 동일한 색깔의 뿌요의 개수를 구한다.
                       int num = count(map[i][j]);

                       if(num >= 4){
                           for(int k=0; k<num; k++){
                               Pos delete = count_queue.poll();
                               map[delete.r][delete.c] = 'd';
                          }
                           // 하나라도 삭제할 게 있으면 while문이 끝나지 않는다.
                           isQuit = false;
                      }
                       count_queue.clear();
                  }
              }
          }

           for(int i=1; i<=N; i++)
               for(int j=1; j<=M; j++){
                   if(map[i][j] == 'd')
                       deletePuyo(new Pos(i, j));
                   check[i][j] = false;
              }
               
           if(isQuit)
               break;

           ++combo;
      }
       System.out.println(combo);
  }
}


'알고리즘' 카테고리의 다른 글

백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 14503 : 로봇 청소기  (0) 2018.09.18
[JAVA] 백준 3190 : 뱀  (1) 2018.09.18

[JAVA] 백준 14503 : 로봇 청소기


문제

로봇 청소기가 있는 장소 N x M 은 빈칸(0) 또는 벽(1)으로 이뤄져있다. 일정한 규칙을 가지고 움직이는 로봇청소기를 이용할 때 청소한 영역의 개수를 구하는 문제이다.로봇청소기의 작동 규칙은 아래와 같다.

    1. 현재 위치를 청소한다.

    1. 현재 위치에서 현재 방향을 기준으로 왼쪽방향부터 차례대로 탐색을 진행한다.

      1) 왼쪽 방향에 아직 청소하지 않은 공간이 존재한다면, 그 방향으로 회전한 다음 한 칸을 전진하고 1번 부터 진행한다. 2) 왼쪽 방향에 청소할 공간이 없다면, 그 방향으로 회전하고 2번으로 돌아간다. 3) 네 방향 모두 청소가 이미 되어있거나 벽인 경우에는, 바라보는 방향을 유지한 채로 한 칸 후진을​ 하고 2번으로 돌아간다. 4) 네 방향 모두 청소가 이미 되어있거나 벽이면서, 뒤쪽 방향이 벽이라 후진도 할 수 없는 경우에는 작동을 멈춘다.


접근방법

- 조건을 그래도 코드로 옮기면 된다. - 청소기의 방향인 북(0), 동(1), 남(2), 서(3) 에서 왼쪽을 보게되면 서(3), 북(0), 동(1), 남(2)을 바라보게 되는데, 이는 switch-case 구문보다 각 방향값에서 1을 빼주는 방법을 사용해 더 깔끔하게 코드를 짤 수 있다.(효근짱b) - 청소기가 네 방향을 모두 탐색한 것은 stay_time 이라는 변수를 두어 해결하였다.


소스코드

import java.util.Scanner;

public class Main{

   static int N;
   static int M;
   static int map[][];
   static int cur_dir;

   static class Pos{ int r; int c; }

   public static int changDir(int dir){
       return (dir == 0)? 3 : dir-1;
  }

   public static Pos changePos(int dir, Pos pos){
       Pos next_pos = new Pos();
       next_pos.r = pos.r;
       next_pos.c = pos.c;

       switch (dir){
           case 0: --next_pos.r; break;    // 북
           case 1: ++next_pos.c; break;    // 동
           case 2: ++next_pos.r; break;    // 남
           case 3: --next_pos.c; break;    // 서
      }
       return next_pos;
  }

   public static Pos backPos(int dir, Pos pos){
       Pos next_pos = new Pos();
       next_pos.r = pos.r;
       next_pos.c = pos.c;

       switch (dir){
           case 0: ++next_pos.r; break;    // 북
           case 1: --next_pos.c; break;    // 동
           case 2: --next_pos.r; break;    // 남
           case 3: ++next_pos.c; break;    // 서
      }
       return next_pos;
  }

   public static int cleaning(int map[][], int N, int M, Pos start){
       int result = 0;

       Pos cur_pos = new Pos();
       Pos before_pos = new Pos();
       cur_pos.c = before_pos.c = start.c;
       cur_pos.r = before_pos.r = start.r;

       int stay_time = 0;
       while(true){
           // 1. 현재 위치를 청소한다.
           if(map[cur_pos.r][cur_pos.c] == 0){
               map[cur_pos.r][cur_pos.c] = 2;
               ++result;
               //printarr(map);
          }

           // 2. 현재 위치에서 현재방향을 기준으로 왼쪽부터 탐색을 진행한다.
           cur_dir = changDir(cur_dir);
           Pos search_pos = changePos(cur_dir, cur_pos);
           ++stay_time;

           // 2-1. 왼쪽방향에 아직 청소하지 않은 공간이 있다면 청소
           if(map[search_pos.r][search_pos.c] == 0){
               cur_pos.r = search_pos.r;   // 현재 위치 변경
               cur_pos.c = search_pos.c;

               stay_time = 0;          // stay time 초기화
          }

           if(stay_time == 4){
               search_pos = backPos(cur_dir, cur_pos);
               // 뒤쪽이 벽이면
               if(map[search_pos.r][search_pos.c] == 1){
                   break;
              }else{
                   // 2-3. 후진
                   cur_pos.r = search_pos.r;
                   cur_pos.c = search_pos.c;
                   stay_time = 0;
              }
          }
      }

       return result;
  }

   private static void printarr(int arr[][]){
       System.out.println();
       for(int i=1; i<arr.length-1; i++){
           for(int j=1; j<arr[0].length-1; j++)
               System.out.print(map[i][j] + " ");
           System.out.println();
      }
  }

   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       N = sc.nextInt();
       M = sc.nextInt();

       map = new int[N+2][M+2];
       for(int i=0; i<N+2; i++)
           for(int j=0; j<M+2; j++)
               map[i][j] = 1;

       Pos start = new Pos();
       start.r = sc.nextInt() + 1;
       start.c = sc.nextInt() + 1;
       cur_dir = sc.nextInt();

       for(int i=1; i<=N; i++)
           for(int j=1; j<=M; j++)
               map[i][j] = sc.nextInt();

       int result = cleaning(map, N, M, start);
       System.out.println(result);
  }
}


'알고리즘' 카테고리의 다른 글

백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20
[JAVA] 백준 3190 : 뱀  (1) 2018.09.18

[JAVA] 백준 3190 : 뱀


문제

지도에 사과가 놓여있고, 뱀은 명령에 따라 정해진 방향으로 1초에 한 번 씩 움직인다. 뱀이 머리를 움직인 곳에 사과가 있으면 꼬리는 그대로 있으면서 몸의 길이가 늘어나고, 사과가 없으면 꼬리가 있는 칸은 비워줘야한다. 

  • 뱀의 처음 몸길이는 1이다.

  • 뱀의 처음위치는 (1,1)이다.

  • 뱀의 처음 이동방향은 오른쪽이다.

  • 사과는 먹고나면 없어진다.

  • 명령은 L, D가 있으며 L은 왼쪽으로, D는 오른쪽으로 90도 방향을 트는 것을 말한다.

  • 예를 들어 현재 방향이 오른쪽이면서 명령이 (3, D)라면, 3초까지 오른쪽으로 이동한 뒤에 4초부터 방향을 90도 틀어 아래로 움직인다.


접근방법

  1. N x N 공간에서 벽을 설정해주기 위해 (N+2) x (N+2) 배열을 만든다.

  2. 공간에서 빈칸은 0, 사과는 1, 벽은 2라고 정해놓고 벽을 따로 설정해준다.

// 벽 세팅
for(int i=0; i<N+2; i++)
for(int j=0; j<N+2; j++)
if(i==0 || j==0 || i==N+1 || j==N+1)
map[i][j] = 2;
  1. 명령어는 X초에 명령 'L' 혹은 'D'가 주어지므로 instruct[X] = 'L' 형식으로 저장한다. 시간을 인덱스로 사용하면 계속해서 경과시간을 나타내는 변수인 time이 증가할 때 참고하기 편하다.

// 명령어 저장
for(int i=0; i<L; i++)
  instruct[sc.nextInt()] = sc.next().charAt(0);
  1. 뱀이 사과를 먹지 않으면 꼬리부터 없어지기 때문에 FIFO방식의 큐를 이용해 뱀의 몸을 저장한다. 뱀은 머리를 움직일 때 마다 머리가 있는 위치를 큐에 넣어주고, 뱀의 머리가 있는 장소에 사과가 있다면 큐에서 꼬리를 꺼내준다. 또한 뱀의 몸 어느 곳이라도 머리가 부딪히면 게임이 끝나도록 하기 위해 큐에 넣을 때에는 해당 공간을 벽과 동일한 2로 변경해준다. 꼬리를 poll() 할 때 역시 빈칸이 되도록 0으로 변경해준다.

  1. 방향 전환은 로봇청소기(14503)과 같이 규칙을 이용한다.

private static int changeDir(int cur_dir, char instruct){
   int next_dir;
   if(instruct == 'D')
       next_dir = (cur_dir==3) ? 0 : ++cur_dir;
   else
       next_dir = (cur_dir==0)? 3 : --cur_dir;

   return next_dir;
}

- 즉, 북(↑) 동(→) 남(↓) 서(←)0 1 2 3 으로 정하고, 오른쪽으로 90도 꺾어져 이동할 때에는 1씩 증가하고, 왼쪽으로 꺾어질 때에는 1씩 감소하는 규칙을 이용한다. (효근짱!b)

  1. 처리하는 순서는 다음과 같다. 1) 시간을 카운트한다. 2) 뱀의 머리 위치를 변경해주고 해당 위치를 큐에 넣는다. 3) 뱀의 머리 위치에 사과가 있는지 확인한다. - 사과가 있다면 아무일도 일어나지 않는다. - 사과가 없다면 큐를 poll() 해준다. 4) 해당 시간에 방g전환 명령이 있는지 확인한다. 명령이 있다면 방향을 바꿔준다.


소스코드

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main{

   static int dr[] = {-1 ,0, 1, 0};
   static int dc[] = {0, 1, 0, -1};

   static class Pos{
       int r;
       int c;
       public Pos(int r, int c){this.r = r; this.c = c;}
  }

   private static int changeDir(int cur_dir, char instruct){
       int next_dir;
       if(instruct == 'D')
           next_dir = (cur_dir==3) ? 0 : ++cur_dir;
       else
           next_dir = (cur_dir==0)? 3 : --cur_dir;

       return next_dir;
  }

   private static Pos getNextPos(Pos pos, int dir){
       Pos next_pos = new Pos(pos.r, pos.c);
       switch (dir){
           case 0: --next_pos.r; break;
           case 1: ++next_pos.c; break;
           case 2: ++next_pos.r; break;
           case 3: --next_pos.c; break;
      }
       return next_pos;
  }


   public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);

       int N = sc.nextInt();   // 보드 크기 (N x N)
       int K = sc.nextInt();   // 사과 개수
       int L;                  // 명령어 개수
       int time = 0;           // 시간초
       int dir = 1;            // 이동 방향 (북-0, 동-1, 남-2, 서-3)

       Queue<Pos> baam = new LinkedList<>();   // 뱀의 몸 좌표를 넣을 큐
       int map[][] = new int[N+2][N+2];        // 보드 (0-빈칸, 1-사과, 2-벽)
       char instruct[] = new char[100001];     // 명령

       // 벽 세팅
       for(int i=0; i<N+2; i++)
           for(int j=0; j<N+2; j++)
               if(i==0 || j==0 || i==N+1 || j==N+1)
                   map[i][j] = 2;
       // 사과 세팅
       for(int i=0; i<K; i++)
           map[sc.nextInt()][sc.nextInt()] = 1;

       L = sc.nextInt();
       // 명령어 저장
       for(int i=0; i<L; i++)
           instruct[sc.nextInt()] = sc.next().charAt(0);

       Pos cur_pos = new Pos(1, 1);
       baam.offer(cur_pos);
       map[cur_pos.r][cur_pos.c] = 2;

       while(true){
           ++time;
           // 뱀의 머리 위치 이동
           cur_pos.r += dr[dir];
           cur_pos.c += dc[dir];
           //cur_pos = getNextPos(cur_pos, dir);

           // 벽이나 뱀의 몸을 만났을 경우 게임 종료
           if(map[cur_pos.r][cur_pos.c] == 2 ){
               break;
          }

           // 빈칸인 경우 마지막 칸 꼬리를 비워줌
           if(map[cur_pos.r][cur_pos.c] == 0){
               Pos retail = baam.poll();
               map[retail.r][retail.c] = 0;
          }
           // 머리를 큐에 넣고 맵의 변수 변경
           baam.offer(cur_pos);
           map[cur_pos.r][cur_pos.c] = 2;

           // 방향전환
           if(instruct[time] == 'D' || instruct[time] == 'L')
               dir = changeDir(dir, instruct[time]);
      }

       System.out.println(time);
  }
}

- 예제 2번의 답을 계속 20이라고 생각했던 이유는 뱀이 1초때에 (1,1)에 있고, 2초때 (1,2)로 이동한다고 생각했기 때문. 뱀은 처음에 (1,1)에 있고 1초 때 (1,2)로 이동하므로 time을 0으로 초기화해줘서 해결 할 수 있었다(성희짱!b) - 시뮬레이션 문제는 특히 푸는데 시간이 많이 걸리는 것 같다. 풀이시간을 계속 줄여나갈 것!

'알고리즘' 카테고리의 다른 글

백준15684 : 사다리 조작  (0) 2018.11.21
백준14889 : 스타트와 링크  (0) 2018.11.16
[JAVA] 백준 3055 : 탈출  (0) 2018.09.27
[JAVA] 백준 11559 : 뿌요뿌요  (0) 2018.09.20
[JAVA] 백준 14503 : 로봇 청소기  (0) 2018.09.18

+ Recent posts