[JAVA] 백준 1986 : 체스

문제


주어진 NxM의 체스판에 Knight, Queen, Pawn이 주어질 때, 이 말들을 움직였을 때 잡힐 수 없는 안전한 칸의 개수를 구하는 문제

Knight : 2x3의 직사각 형을 만들 었을 때, 해당 점에서 가장 먼 점으로만 이동할 수 있다. 중간에 다른 말이 있어도 영향을 받지 않는다.

Queen : 가로, 세로, 대각선으로 쭉 움직일 수 있다. 단, 중간에 다른 말이 있다면 그 전까지만 이동할 수 있다.

Pawn : 움직이지 못하고 그저 장애물 역할만 한다.



접근방법


주어진대로 노가다 코드를 짜면 얼마든지 해결할 수 있다.


즉, 먼저 Queen이 갈 수 있는 범위를 체크하고, 그 다음으로 Knight가 갈 수 있는 범위를 체크하고, 해당 말 들이 없는 칸들의 개수를 세서 출력해주면 된다.

  1. 먼저 queen, knight의 위치를 ArrayList에 저장하고, char배열인 맵의 해당 위치에 각 말의 알파벳(K, Q, P)을 넣어준다.

  2. 각 queen, knight를 정해진 규칙에 따라 이동하도록 하고, 이동한 좌표는 'o'로 바꾸어 준다.

  3. 각 말들이 이동하지 않은 곳은 char의 초기값인 '\u0000'이 들어있으므로, 이러한 원소들의 개수를 확인해 출력해준다.


같은 스터디원의 코드를 참고해 더 깔끔하게 코드를 수정해 보았다.

  1. dx, dy 배열을 이용하면 규칙이 없더라도 for문으로 돌릴 수 있다.

    • 예를 들어 위, 아래, 오른쪽, 왼쪽, 왼쪽위, 오른쪽위, 왼쪽아래, 오른쪽아래를 확인해야 하는 Queen의 경우에는 dx, dy 배열을 이용해 아래와 같이 코드를 짤 수 있다.

      for(int i=0; i<8; i++){
         int next_x = x;
         int next_y = y;
         while(true) {
             next_x += dx[i];
             next_y += dy[i];

             if(next_x < 0 || next_x >= N || next_y < 0 || next_y >= M)
                 break;  // map 범위를 벗어나는 경우
             if(map[next_x][next_y] == 'K'
                     || map[next_x][next_y] == 'Q'
                     || map[next_x][next_y] == 'P' )
                 break;  // 말이 지나갈 수 없는 경우(K/P/Q)

             map[next_x][next_y] = 'o';
        }
      }
    • knight의 움직임 경우에는 아래와 같이 코드를 짤 수 있다.

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

      for(int i=0; i<8; i++){
         int next_x = x + dx[i];
         int next_y = y + dy[i];

         if(next_x >= 0 && next_x < N
                 && next_y >=0 && next_y < M){   // 배열 범위 내에 있으면서
             if(map[next_x][next_y] == '\u0000') // 빈칸일 경우
                 map[next_x][next_y] = 'o';
        }
      }

소스코드

    static int N, M;
   static char[][] map;

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

   static void checkQueen(int x, int y){

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

       for(int i=0; i<8; i++){
           int next_x = x;
           int next_y = y;
           while(true) {
               next_x += dx[i];
               next_y += dy[i];

               if(next_x < 0 || next_x >= N || next_y < 0 || next_y >= M)
                   break;  // map 범위를 벗어나는 경우
               if(map[next_x][next_y] == 'K'
                       || map[next_x][next_y] == 'Q'
                       || map[next_x][next_y] == 'P' )
                   break;  // 말이 지나갈 수 없는 경우(K/P/Q)

               map[next_x][next_y] = 'o';
          }
      }
  }

   static void checkKnight(int x, int y){
       int[] dx = {2, 2, 1, 1, -1, -1, -2, -2};
       int[] dy = {1, -1, 2, -2, 2, -2, 1, -1};

       for(int i=0; i<8; i++){
           int next_x = x + dx[i];
           int next_y = y + dy[i];

           if(next_x >= 0 && next_x < N
                   && next_y >=0 && next_y < M){   // 배열 범위 내에 있으면서
               if(map[next_x][next_y] == '\u0000') // 빈칸일 경우
                   map[next_x][next_y] = 'o';
          }
      }
  }

   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 char[N][M];

       ArrayList<Pos> queen = new ArrayList<>();
       ArrayList<Pos> knight = new ArrayList<>();

       // input
       for(int i=0; i<3; i++){
           input = in.readLine().split(" ");
           int num = Integer.parseInt(input[0]);

           for(int j=1; j<=num; j++){
               int x = Integer.parseInt(input[2*j-1]) - 1;
               int y = Integer.parseInt(input[2*j]) - 1;

               if(i==0){
                   queen.add(new Pos(x,y));
                   map[x][y] = 'Q';
              }else if(i==1){
                   knight.add(new Pos(x,y));
                   map[x][y] = 'K';
              }else{
                   map[x][y] = 'P';
              }
          }
      }

       // queen check
       for(int i=0; i<queen.size(); i++)
           checkQueen(queen.get(i).x, queen.get(i).y);


       // knight check
       for(int i=0; i<knight.size(); i++)
           checkKnight(knight.get(i).x, knight.get(i).y);

       int answer = 0;
       for(int i=0; i<N; i++)
           for(int j=0; j< M; j++)
               if(map[i][j] == '\u0000')
                   ++answer;

       System.out.println(answer);
  }

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

[JAVA] 백준 1406 : 에디터  (0) 2019.02.16
[JAVA] 백준 1347 : 미로만들기  (0) 2019.02.06
[JAVA] 백준 2512 : 예산  (0) 2019.02.02
[JAVA] 백준 3985 : 롤케이크  (0) 2019.02.01
[JAVA] 백준 10610 : 30  (0) 2019.02.01

[JAVA] 백준 1406 : 에디터


문제

명령어를 받아 문자열을 수정하는 에디터를 작성하는 문제. 처음 문자열과 명령어 개수, 명령어 L, D, B, P $가 입력된다. 커서를 이용해 문자열을 수정하는데, 처음 커서는 문자열의 맨 뒤에 있다. L의 경우에는 커서를 왼쪽으로 하나, D의 경우에는 커서를 오른쪽으로 하나, B는 커서의 왼쪽 문자를 삭제하고, P ~ 의 경우에는 해당 문자를 커서 뒤에 추가해준다. 커서는 보통 컴퓨터에서 움직이는 것 처럼 움직인다.


처음 접근방법

cursor를 String의 인덱스로 이용해 문제 그대로 구현하였고, 시간초과가 났다. StringBuffer을 이용해도 시간초과가 났기 때문에 구현하는 방법을 바꿔야 했다.


수정된 접근방법

  • 스택 2개를 이용한다.

    • 커서를 스택 A의 pop위치라고 두고, 스택 B는 커서 뒤에 있는 문자를 저장한다.

    • 명령어 수행 방법

      • L : 스택 A를 스택 B로 옮긴다. stack_B.push(stack_A.pop());

      • 명령어 D : 스택 B를 스택 A로 옮긴다. stack_A.push(stack_B.pop());

      • 명령어 B : 스택 A를 pop 해준다.

      • 명렁어 P : 스택 A에 문자를 push 해준다.

    • 여기서 stack이 비어있을 경우를 꼭 확인해 주어야한다.




소스코드

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String text = in.readLine();
int N = Integer.parseInt(in.readLine());

Stack<Character> stack = new Stack<>();
Stack<Character> stack_tmp = new Stack<>();

for(int i=0; i<text.length(); i++)
stack.push(text.charAt(i));

for(int i=0; i<N; i++){
String cmd = in.readLine();

if(cmd.charAt(0) == 'L'){
if(!stack.isEmpty())
stack_tmp.push(stack.pop());
}else if(cmd.charAt(0) == 'D'){
if(!stack_tmp.isEmpty())
stack.push(stack_tmp.pop());
}else if(cmd.charAt(0) == 'B'){
if(!stack.isEmpty())
stack.pop();
}else if(cmd.charAt(0) == 'P'){
stack.push(cmd.charAt(2));
}
}

while(!stack_tmp.isEmpty()){
stack.push(stack_tmp.pop());
}

StringBuilder answer = new StringBuilder();
while(!stack.isEmpty()){
answer.append(stack.pop());
}

System.out.println(answer.reverse().toString());





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

[JAVA] 백준 1986 : 체스  (0) 2019.02.21
[JAVA] 백준 1347 : 미로만들기  (0) 2019.02.06
[JAVA] 백준 2512 : 예산  (0) 2019.02.02
[JAVA] 백준 3985 : 롤케이크  (0) 2019.02.01
[JAVA] 백준 10610 : 30  (0) 2019.02.01

문제

홍준이는 미로의 남쪽을 보면서 서 있으며 명령에 따라 움직인다. F가 나오면 해당 방향으로 이동하고, R이나 L이 나오면 현재 방향을 기준으로 왼쪽이나 오른쪽으로 방향을 바꾼다. 명령에 따라 움직인 것은 미로에서 갈 수 있는 모든 곳이라고 했을 때, 갈 수 있는 길은 '.'로, 갈 수 없는 길은 '#'로 미로를 출력한다.



접근방법

  1. 처음 좌표를 (0,0)으로 두고, 이동한 좌표를 저장한다.

    • 현재 방향을 direct로 두고, 방향 명령어(R, L)이 나오면 이 방향을 바꿔준다.

    • 이동 명령어(F)가 들어오면 현재 위치를 기준으로 해당 방향으로 이동한 좌표를 리스트에 저장한다.

      ex) 현재 좌표 (0, 1)이며 현재 방향이 위쪽일 때 이동명령어(F)가 들어오면 (-1, 1) 좌표를 만들어 저장한다.

  1. x, y 각각의 max, min 값을 저장 한 뒤, 이 값을 이용해 배열의 크기를 정한다.

    • (0, 0), (-1, 0), (-1, 1) 로 이동했다고 할 때, 만들어지는 배열의 크기는 (max_x - min_x + 1) * (max_y - min_y + 1)이 된다.

    • 주어진 조건에서 '모든 행과 열에는 적어도 하나의 이동할 수 있는 칸이 있다'고 했으므로, 이동한 칸을 중심으로 배열을 만들고 그 중에서 이동하지 않은 칸은 벽('#')으로 채워주면 된다.

    • int[][] map = new int[max_x - min_x + 1][max_y - min_y + 1];
  2. 이동한 좌표의 가장 왼쪽 위의 값을 (0, 0)으로 바꾸어 배열에 넣는다.

    • 처음 (0, 0) 을 기준으로 상대좌표를 저장하고 있기 때문에, 모든 좌표에서 min_x를 0으로, min_y를 0으로 만들면 가장 작은 값이 왼쪽 위가 되므로 절대 좌표로 바꾸어준다.

      for(int i=0; i<poses.size(); i++){
         int x = poses.get(i).x + (-1 * min_x);
         int y = poses.get(i).y + (-1 * min_y);
         map[x][y] = 1;
      }
  1. 이동한 좌표를 배열에 표시한 다음, 주어진 출력 형식에 맞춰 출력한다.



소스코드

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

static Pos cur_pos;
static int direct; // 방향 : 위(0), 오른(1), 아래(2), 왼(3)

// 방향 회전
static void rotate(char dir){
   if(dir == 'R'){
       ++direct;
       if(direct > 3)
           direct = 0;
  }else if(dir == 'L'){
       --direct;
       if(direct < 0)
           direct = 3;
  }
}

public static void main(String[] args) throws IOException{
   BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
   int N = Integer.parseInt(in.readLine());
   String command = in.readLine();

   cur_pos = new Pos(0,0); // 처음 좌표 : (0, 0)
   direct = 2; // 처음 방향 : 남쪽

   ArrayList<Pos> poses = new ArrayList<>();
   poses.add(new Pos(0,0));

   int min_x, min_y, max_x, max_y;
   min_x = min_y = max_x = max_y = 0;

   for(int i=0; i<N; i++){
       // 해당 방향으로 이동
       if(command.charAt(i) == 'F'){
           if(direct == 0)
               --cur_pos.x;
           else if(direct == 1)
               ++cur_pos.y;
           else if(direct == 2)
               ++cur_pos.x;
           else if(direct == 3)
               --cur_pos.y;

           poses.add(new Pos(cur_pos.x, cur_pos.y));

           // 최대값-최솟값 저장
           if(cur_pos.x > max_x) max_x = cur_pos.x;
           else if(cur_pos.x < min_x) min_x = cur_pos.x;
           if(cur_pos.y > max_y) max_y = cur_pos.y;
           else if(cur_pos.y < min_y) min_y = cur_pos.y;

      }
       else{ // 방향 전환
           rotate(command.charAt(i));
      }

  }

   int[][] map = new int[max_x - min_x + 1][max_y - min_y + 1];

   // 가장 왼쪽 위 좌표 (0,0)으로 만들기
   for(int i=0; i<poses.size(); i++){
       int x = poses.get(i).x + (-1 * min_x);
       int y = poses.get(i).y + (-1 * min_y);
       map[x][y] = 1;
  }


   // answer
   for(int i=0; i<map.length; i++){
       for(int j=0; j<map[0].length; j++){
           if(map[i][j] == 1)
               System.out.print('.');
           else
               System.out.print("#");
      }
       System.out.println();
  }
}


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

[JAVA] 백준 1986 : 체스  (0) 2019.02.21
[JAVA] 백준 1406 : 에디터  (0) 2019.02.16
[JAVA] 백준 2512 : 예산  (0) 2019.02.02
[JAVA] 백준 3985 : 롤케이크  (0) 2019.02.01
[JAVA] 백준 10610 : 30  (0) 2019.02.01

문제

지방의 수 N(3<=N<=10000), 각 지방의 요청 예산, 그리고 전체 예산이 주어졌을 때,

  1. 모든 요청이 배정될 수 있는 경우에는 요청한 금액 그대로 배정한다.

  2. 모든 요청이 배정될 수 없으면 특정한 정수 상한액을 계산하여 배분한다.

위 규칙에 따라 예상을 배정할 때, 가장 크게 산정될 수 있는 상한액을 계산하는 문제



접근방법

  1. 각 지방의 예산region[i] 을 모두 할당할 지, 상한액을 정해야할 지 판단하기 위해서는 ( 남은 지방의 수 * 해당 지방의 예산 ) 이 남은 예산을 초과하는지를 확인하면 된다.

    • 예를 들어 각 지방 요청예산이 [110, 120, 140, 150] 이며, 전체 예산이 485일 때, region[0] 을 볼 때, 나머지 예산들은 모두 110보다는 같거나 크므로, 가장 작아봐야 모두 110이 된다. 이 때, 110*4 는 전체 예산인 485를 넘지 않으므로 110을 모두 할당할 수 있다.

    • 110을 할당한 뒤, 전체 예산은 375가 남고, 그 다음 지방예산인 120을 본다. 남은 지방은 3개가 남았고 모두 가장 작아도 120이므로, 120 * 3을 하면 역시 남은 예산인 375보다 작으므로 해당 지방에도 예산을 모두 배정할 수 있다.

    • 다음 3번째 지방을 보면, 남은 예산은 255가 남았으며 지방은 2개가 남았다. 140 * 2를 하면 남은 예산을 초과하게 되므로 예산을 모두 할당해 줄 수 없고 상한선을 설정해야 한다.

    • 상한선은 남은 예산을 최대한 많이 사용하는 값으로 정해야 하므로, 몇 개의 지방에 배정해주고 남은 예산을 남은 지방에 동일하게 나누어주고 나머지를 버리면 최대 상한값을 얻을 수 있다.


  1. 모든 예산을 배정해줄 수 있을 때의 상한값은 가장 큰 지방 요청 예산이 된다.

    • 배열을 정렬하면 상한값은 region[N-1]이 된다.



소스코드

Scanner sc = new Scanner(System.in);

int N = sc.nextInt();
int[] region = new int[N];
for(int i=0; i<N; i++)
    region[i] = sc.nextInt();

Arrays.sort(region);

int budget = sc.nextInt();
int limit = 0;
for(int i=0; i<N; i++){
    if(region[i] * (N-i) < budget){
        budget -= region[i];
    }else{
        limit = budget/(N-i);
        break;
    }
}

// 모두 예산을 배정할 수 있는 경우
if(limit == 0)
    limit = region[N-1];

System.out.println(limit);


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

[JAVA] 백준 1406 : 에디터  (0) 2019.02.16
[JAVA] 백준 1347 : 미로만들기  (0) 2019.02.06
[JAVA] 백준 3985 : 롤케이크  (0) 2019.02.01
[JAVA] 백준 10610 : 30  (0) 2019.02.01
[JAVA] 백준 2980 : 도로와 신호등  (0) 2019.01.21

문제

롤케이크를 L개로 잘랐고, N명의 사람들이 원하는 케이크 조각 번호를 적어 내면 순서대로 조각을 차지하게 된다. 이미 차지한 케이크는 갖지 못하고 그 조각을 제외한 부분만 받게 된다.



접근방법

  1. 처음 사람들이 기대했던 케이크 양을 저장하고, 순서대로 케이크를 차지하게 한 다음, 실제 케이크를 차지한 결과를 확인한다.

  2. 각각의 조각을 먹은 사람들의 인덱스가 저장되어 있는 케이크 배열 cake[], 처음 기대했던 케이크의 양을 가지고 있는 배열 people_before[], 실제로 차지한 케이크 양을 저장하는 people_after[] 배열 사용한다.

  3. 각 케이크에 초기값 0이 아닌 수가 있으면 먹을 수 없으므로, 0일 때에만 값을 바꿔준다.

  4. 최대 케이크 양이 아니라, 케이크를 차지한 사람을 알아야 하므로, max 값과 index 값을 함께 저장하며 for문을 돈다.



소스코드

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

       Scanner sc = new Scanner(System.in);
       int L = sc.nextInt();
       int N = sc.nextInt();
       int[] cake = new int[L+1];           // 케이크 차지 현황
       int[] people_before = new int[N+1];  // 처음 기대했던 케이크 양
       int[] people_after = new int[N+1];   // 실제로 받은 케이크 양

       for(int i=1; i<=N; i++){
           int start = sc.nextInt();
           int end = sc.nextInt();
           // 기대하는 케이크 양 저장
           people_before[i] = end - start + 1;

           // 케이크 차지
           for(int j=start; j<=end; j++)
               if(cake[j] == 0)
                   cake[j] = i;
      }

       // 실제 받은 케이크 양 저장
       for(int i=1; i<=L; i++){
           if(cake[i] != 0)
               ++people_after[cake[i]];
      }

       // 기대, 실제 각각의 max 인덱스 계산
       int max_before = 0, index_before = 0;
       int max_after = 0, index_after = 0;
       for(int i=1; i<=N; i++){
           if(people_before[i] > max_before) {
               max_before = people_before[i];
               index_before = i;
          }
           if(people_after[i] > max_after) {
               max_after = people_after[i];
               index_after = i;
          }
      }

       System.out.println(index_before);
       System.out.println(index_after);

  }


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

[JAVA] 백준 1347 : 미로만들기  (0) 2019.02.06
[JAVA] 백준 2512 : 예산  (0) 2019.02.02
[JAVA] 백준 10610 : 30  (0) 2019.02.01
[JAVA] 백준 2980 : 도로와 신호등  (0) 2019.01.21
[JAVA] 백준 2593 : 영역 구하기  (0) 2019.01.20

문제

양수 N(최대 105개의의 숫자)가 있을 때 그 수의 조합을 섞어 가장 큰 30의 배수를 만들어 출력하는 문제. 30의 배수가 만들어지지 않으면 -1을 출력한다.



접근방법

  1. 3의 배수는 각 자리수의 합이 3의 배수이며, 10의 배수는 끝이 0으로 끝나는 것을 이용한다.

    • 즉, 각 자리수를 더한 합이 3의 배수이며 0이 포함되어있으면 30의 배수를 만들 수 있는 수이다.

    • 위의 식이 성립하지 않으면 -1을 출력한다.

  2. 배열을 정렬한 뒤 가장 뒤에서부터 출력하면 가장 큰 30의 배수를 출력할 수 있다.



소스코드

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

       Scanner sc = new Scanner(System.in);
       String input = sc.next();

       int[] N = new int[input.length()];
       int sum = 0;    // 숫자들의 합
       boolean isZero = false; // 0존재 여부
       
       for(int i=0; i<input.length(); i++){
           N[i] = Integer.parseInt(input.charAt(i)+"");
           sum += N[i];
           if(N[i] == 0)
               isZero = true;
      }

       Arrays.sort(N);
       if(sum%3 == 0 && isZero){
           for(int i=N.length-1; i>=0; i--)
               System.out.print(N[i]);
      }else{
           System.out.print(-1);
      }

  }


문제

신호등의 개수(N), 도로의 길이(L)가 주어져 있으며 각 신호등의 도로 위의 위치와 빨간불, 초록불이 유지되는 시간이 주어져 있다. 트럭이 1초에 1m씩 이동하고, 신호를 지켜 도로(Lm)를 다 지나갈 때 몇 초가 걸리는지 계산하는 문제.



접근방법

  1. 신호등 데이터를 도로 배열에 저장한다.

    • Traffic_light라는 클래스를 새로 만들고, 여기에는 빨간불 유지 시간(R), 초록불 유지시간(G)를 가지고 있다.

    • Traffic_light를 타입으로 하는 배열을 선언하고, 신호등의 위치(D)에 신호등 데이터(R, G)를 저장한다. load[D] = new Traffic_light(R, G)

  1. 일정한 규칙으로 반복되는 신호등이 x초일 때 얼마나 기다려야 하는지를 계산한다.

    • 나머지 연산을 이용해 계산한다.

    • 예를 들어 (4, 2)로 반복되는 신호등은 xxxxoo가 반복되는데 time=7인 경우에는 7%6로 해서 첫번째 x에 해당된다.

    • 남은 시간을 계산할 때에는 전체 R에서 7%6인 1을 뺀 값을 더해준다. 즉, 3초를 기다리므로 3을 더해준다.



소스코드

static class Traffic_light{
   int R; int G;
   public Traffic_light(int r, int g) {this.R = r; this.G = g;}
}

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

   String[] input = in.readLine().split(" ");
   int N = Integer.parseInt(input[0]);
   int L = Integer.parseInt(input[1]);

   // 도로 위 신호등 확인 배열
   Traffic_light[] load = new Traffic_light[L+1];

   for(int i=0; i<N; i++){
       String[] input_data = in.readLine().split(" ");
       int D = Integer.parseInt(input_data[0]);
       int R = Integer.parseInt(input_data[1]);
       int G = Integer.parseInt(input_data[2]);

       load[D] = new Traffic_light(R, G);
  }

   int time = 0;
   int pos = 0;
   while(pos < L){

       ++time; //시간초 증가
       ++pos;  // 다음 위치로 이동

       // 다음에 이동할 곳에 신호등이 있는 경우
       if(load[pos] != null){

           int check = time % (load[pos].R + load[pos].G);

           // 현재 빨간불인 경우 기다렸다가 지나감
           if(check <= load[pos].R){
               time += (load[pos].R - check);
          }
      }
  }

   System.out.println(time);

}


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

[JAVA] 백준 3985 : 롤케이크  (0) 2019.02.01
[JAVA] 백준 10610 : 30  (0) 2019.02.01
[JAVA] 백준 2593 : 영역 구하기  (0) 2019.01.20
[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13

문제

눈금 간격이 MxN인 크기의 모눈종이 위에 눈금에 맞춰 K개의 직사각형을 그릴 때, K개의 직사각형의 내부를 제외한 나머지 부분이 몇 개의 분리된 영역으로 나누어지는지 구하고, 각각 영역의 크기를 오름차순으로 출력하는 문제.

※ 예제 : (0,2) (4,4) , (1,1) (2,5) , (4,0) (6,2) 로 입력을 받았음.



접근방법

  1. 주어진 모눈종이는 보통 사용하는 이차원 배열과는 조금 다르게 생겼다. 이차원배열로 구현하기 위해 조금 바꾸어준다.

    1) 왼쪽 위가 (0,0)이 되도록 모눈종이를 뒤집어도, 결과 영역은 달라지는게 없으므로, 원래 배열과 같이 (0,0)을 가장 위로 생각하고 입력을 받는다.

    2) 이차원배열과 달리 모눈종이의 '점'을 입력으로 받기 때문에, 이를 배열의 '칸'으로 바꿔주어야 한다.

    • 각 점이 아니라, 각 칸을 기준으로 좌표를 계산하면 처음 입력했던 (0,2)는 (2,0)에 들어가며, (4,4)는 (3,3)에 들어간다. 즉, 마지막 점을 배열의 칸으로 변경하면 해당 점의 왼쪽 위 좌표와 동일하므로, 행, 열에서 1씩 빼준 값을 넣어주면 된다.

  2. 영역의 개수를 구하는 부분은 BFS를 이용한다.

  3. 영역별 크기를 구하기 위해서는 BFS 함수를 실행할 때 size를 체크해준다. 즉, 큐에 원소를 넣을 때 마다 size를 증가시켜준다.

  4. 영역별 크기를 오름차순으로 출력하기 위해서 Collection을 이용한다. ArrayList를 정렬하는 코드 : Collections.sort(sizeList);



소스코드

public class Main {

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

   static int[] dx = {1, -1, 0, 0};
   static int[] dy = {0, 0, 1, -1};
   static int result = 0;
   static int N, M, K;
   static boolean[][] visited;
   static int[][] map;
   static Queue<Pos> queue;
   static ArrayList<Integer> sizeList;

   static void BFS(){
       int size = 1;
       while(!queue.isEmpty()){
           Pos cur_pos = queue.poll();

           for(int i=0; i<4; i++){
               Pos next_pos = new Pos(cur_pos.x + dx[i], cur_pos.y + dy[i]);
               if(map[next_pos.x][next_pos.y] != 1 && !visited[next_pos.x][next_pos.y]){
                   visited[next_pos.x][next_pos.y] = true;
                   queue.offer(next_pos);
                   ++size;
              }
          }
      }
       sizeList.add(size);
  }

   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]);
       K = Integer.parseInt(input[2]);

       visited = new boolean[N+2][M+2];
       map = new int[N+2][M+2];
       
       // 벽 설정(배열범위 초과 방지)
       for(int i=0; i<=N+1; i++)
           for(int j=0; j<=M+1; j++)
               if(i==0 || i==N+1 || j==0 || j==M+1)
                   map[i][j] = 1;

       // 색칠된 부분 1로 변경
       for (int i = 0; i < K; i++) {
           String[] pos = in.readLine().split(" ");
           int start_y = Integer.parseInt(pos[0]);
           int start_x = Integer.parseInt(pos[1]);
           int end_y = Integer.parseInt(pos[2])-1;
           int end_x = Integer.parseInt(pos[3])-1;

           for (int r = start_x; r <= end_x; r++)
               for (int c = start_y; c <= end_y; c++)
                   map[r][c] = 1;
      }

       // BFS
       sizeList = new ArrayList<>();
       queue = new LinkedList<>();
       for(int i=1; i<=N; i++)
           for(int j=1; j<=M; j++)
               if(map[i][j] == 0 && !visited[i][j]){
                   ++result;
                   visited[i][j] = true;
                   queue.offer(new Pos(i, j));
                   BFS();
              }

       // 각 영역별 크기 정렬
       Collections.sort(sizeList);
       System.out.println(result);
       for(int i=0; i<sizeList.size(); i++)
           System.out.print(sizeList.get(i) + " ");

  }

}


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

[JAVA] 백준 10610 : 30  (0) 2019.02.01
[JAVA] 백준 2980 : 도로와 신호등  (0) 2019.01.21
[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준15686 : 치킨 배달  (0) 2018.11.23

문제

NxM의 체스판이 주어졌을 때(8 <= N, M <= 20), 보드를 잘라 8x8의 체스판을 만들려고 한다. 이 때 체스판의 흰x백 배열을 만들기 위해 최소로 다시 색칠해야하는 칸의 개수를 구하는 문제.



접근방법

  1. 체스판을 8x8씩 돌아가면서 다시 칠해야 하는 개수를 체크한다.

  2. 체스판의 첫번째 칸 색에 따라 동일한 규칙을 적용받는 방법을 이용한다.

    즉, 첫번째 칸이 'W'일 경우에는 (배열이 0부터 시작한다고 하면, )짝수열의 짝수행은 W, 홀수행은 B가 되어야 하며, 홀수열의 짝수행은 B, 홀수행은 W가 되어야 한다.

  3. 첫번째칸이 W인 경우와 B인 경우를 고려해 더 적은 수를 선택하여, 전체 중 가장 작은 수를 구한다. (처음에는 첫번째 칸의 색을 기준으로 비교를 했지만, 첫번째 칸을 다른 색으로 칠했을 때 더 조금만 칠해서 체스판을 만들 수 있는 경우가 있으므로, 첫번째 칸의 색에 관계 없이 모두 확인해 주어야 한다.)




소스코드

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

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

   char[][] map = new char[N][M];
   for(int i=0; i<N; i++){
       String line = in.readLine();
       for(int j=0; j<M; j++)
           map[i][j] = line.charAt(j);
  }

   int min = 64;
   for(int i=0; i<N-8+1; i++){
       for(int j=0; j<M-8+1; j++){

           // B로 시작하는 체스판과 비교
           int diff_B = 0;
           for(int a=0; a<8; a++) {
               for (int b = 0; b < 8; b++) {
                   // B로 시작하면 짝수열(0,2..) - 짝수행 'B', 홀수행 'W'
                   //           홀수열(1,3..) - 짝수행 'W', 홀수행 'B'
                   if (a % 2 == 0) {
                       if (b % 2 == 0) {
                           if (map[i + a][j + b] != 'B')
                               ++diff_B;
                      } else {
                           if (map[i + a][j + b] != 'W')
                               ++diff_B;
                      }
                  } else {
                       if (b % 2 == 0) {
                           if (map[i + a][j + b] != 'W')
                               ++diff_B;
                      } else {
                           if (map[i + a][j + b] != 'B')
                               ++diff_B;
                      }
                  }
              }
          }

           // W로 시작하는 체스판과 비교
           int diff_W = 0;
           for(int a=0; a<8; a++)
                   for(int b=0; b<8; b++){
                       // W로 시작하면 짝수열(0,2..) - 짝수행 'W', 홀수행 'B'
                       //           홀수열(1,3..) - 짝수행 'B', 홀수행 'W'
                       if(a%2 == 0){
                           if(b%2 == 0){
                               if(map[i+a][j+b] != 'W')
                                   ++diff_W;
                          }else{
                               if(map[i+a][j+b] != 'B')
                                   ++diff_W;
                          }
                      }else{
                           if(b%2 == 0){
                               if(map[i+a][j+b] != 'B')
                                   ++diff_W;
                          }else{
                               if(map[i+a][j+b] != 'W')
                                   ++diff_W;
                          }
                      }
                  }

           // 최솟값 비교
           int dif_min = (diff_W < diff_B) ? diff_W : diff_B;
           if(dif_min < min)
               min = dif_min;
      }
  }
   System.out.println(min);
}


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

[JAVA] 백준 2980 : 도로와 신호등  (0) 2019.01.21
[JAVA] 백준 2593 : 영역 구하기  (0) 2019.01.20
[JAVA] 백준 1120 : 문자열  (0) 2019.01.13
백준15686 : 치킨 배달  (0) 2018.11.23
백준16234 : 인구 이동  (0) 2018.11.21

[JAVA] 백준 1120 : 문자열

문제 : 두 문자열 A, B가 주어질 때(A의길이 <= B의 길이), A앞에 아무 알파벳이나 추가하고, A뒤에 아무 알파벳이나 추가하여 B와 길이가 같게 만들어 A[i] B[i]의 차이가 최소인 문자열을 만들고, 그 차이를 출력하는 문제


접근방법

  1. 주어진 A를 B와 일치하는 알파벳이 가장 많은 곳에 위치시키고, 나머지를 B와 동일한 알파벳으로 채우면 차이가 최소가 된다.

    예를들어, abc, cbabcaa가 주어질 때

    __abc__ cbabcaa 와 같이 abc를 위치시키면,

    A의 앞뒤를 B와 동일하게 cbabcaa로 만들면 차이가 최소가 된다.

  2. A의 앞뒤를 B와 동일하게 만들면 새로 추가한 부분에 대한 차이값은 0이므로, 별도로 고려해주지 않아도 된다.

    즉, A와 B가 가장 많이 일치하는 위치를 찾고, 그 때의 차이값을 출력해주면 된다.

  3. B에서 A의 길이만큼의 부분문자열을 얻어와 A와 비교하는 방법을 사용한다.

    String temp_B = B.substring(i, i+A.length());
    int same = 0;
    for(int j=0; j<temp_B.length(); j++){
       if(A.charAt(j) == temp_B.charAt(j))
           ++same;
    }

소스코드

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String input[] = in.readLine().split(" ");
String A = input[0];
String B = input[1];

int max_same = 0;
for(int i=0; i<B.length()-A.length()+1; i++){
   String temp_B = B.substring(i, i+A.length());
   int same = 0;
   for(int j=0; j<temp_B.length(); j++){
       if(A.charAt(j) == temp_B.charAt(j))
           ++same;
  }
   if(same > max_same)
       max_same = same;
}

System.out.println(A.length() - max_same);


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

[JAVA] 백준 2593 : 영역 구하기  (0) 2019.01.20
[JAVA] 백준 1018 : 체스판 다시 칠하기  (0) 2019.01.16
백준15686 : 치킨 배달  (0) 2018.11.23
백준16234 : 인구 이동  (0) 2018.11.21
백준15684 : 사다리 조작  (0) 2018.11.21

+ Recent posts