7.2 쿼드 트리 뒤집기(ID : QUADTREE)

제출일 : 2021-01-12

문제 풀이 시간 : 1H 30M


Link : https://algospot.com/judge/problem/read/QUADTREE

문제

대량의 좌표 데이터를 메모리 안에 압축해 저장하기 위해 사용하는 여러 기법 중 쿼드 트리(quad tree)란 것이 있습니다. 주어진 공간을 항상 4개로 분할해 재귀적으로 표현하기 때문에 쿼드 트리라는 이름이 붙었는데, 이의 유명한 사용처 중 하나는 검은 색과 흰 색밖에 없는 흑백 그림을 압축해 표현하는 것입니다. 쿼드 트리는 2N × 2N 크기의 흑백 그림을 다음과 같은 과정을 거쳐 문자열로 압축합니다.

  • 이 그림의 모든 픽셀이 검은 색일 경우 이 그림의 쿼드 트리 압축 결과는 그림의 크기에 관계없이 b가 됩니다.
  • 이 그림의 모든 픽셀이 흰 색일 경우 이 그림의 쿼드 트리 압축 결과는 그림의 크기에 관계없이 w가 됩니다.
  • 모든 픽셀이 같은 색이 아니라면, 쿼드 트리는 이 그림을 가로 세로로 각각 2등분해 4개의 조각으로 쪼갠 뒤 각각을 쿼드 트리 압축합니다. 이때 전체 그림의 압축 결과는 x(왼쪽 위 부분의 압축 결과)(오른쪽 위 부분의 압축 결과)(왼쪽 아래 부분의 압축 결과)(오른쪽 아래 부분의 압축 결과)가 됩니다. 예를 들어 그림 (a)의 왼쪽 위 4분면은 xwwwb로 압축됩니다.

그림 (a)와 그림 (b)는 16×16 크기의 예제 그림을 쿼드 트리가 어떻게 분할해 압축하는지를 보여줍니다. 이때 전체 그림의 압축 결과는 xxwww bxwxw bbbww xxxww bbbww wwbb가 됩니다.

쿼드 트리로 압축된 흑백 그림이 주어졌을 때, 이 그림을 상하로 뒤집은 그림 을 쿼드 트리 압축해서 출력하는 프로그램을 작성하세요.

입력

첫 줄에 테스트 케이스의 개수 C (C≤50)가 주어집니다. 그 후 C줄에 하나씩 쿼드 트리로 압축한 그림이 주어집니다. 모든 문자열의 길이는 1,000 이하이며, 원본 그림의 크기는 $2^{20} × 2^{20}$ 을 넘지 않습니다.

출력

각 테스트 케이스당 한 줄에 주어진 그림을 상하로 뒤집은 결과를 쿼드 트리 압축해서 출력합니다.

예제

입력

4
w
xbwwb
xbwxwbbwb
xxwwwbxwxwbbbwwxxxwwbbbwwwwbb

출력

w
xwbbw
xxbwwbbbw
xxwbxwwxbbwwbwbxwbwwxwwwxbbwb

문제 풀이 접근

풀이가 떠오르지 않아 QuadTree 클래스를 만들어 단순하지만 무식한 방법으로 해결하려 접근하였습니다. 하지만 각 부분의 길이가 일정하지 않기 때문에 쪼개기가 까다로웠고 다른 풀이방법을 생각해야 했습니다.

이 문제를 분할하여 상하 반전을 한 값을 다시 병합했을때 결과가 정답이 된다는 것을 확인하고 아래와 같이 코드를 작성하였습니다.

코드

언어 : JAVA

실행 시간 : 156ms

import java.util.Scanner;

public class Main {
    static String str;
    static int pointer;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int C = sc.nextInt();
        for (int tc = 0; tc < C; tc++) {
            str = sc.next();
            pointer = 0;
            System.out.println(reverse());
        }
    }

    private static String reverse() {
        if (str.charAt(pointer) != 'x') {
            return "" + str.charAt(pointer++);
        }
        pointer++;
        String one = reverse();
        String two = reverse();
        String three = reverse();
        String four = reverse();
        return 'x' + three + four + one + two;
    }
}

'Problem' 카테고리의 다른 글

6.8 시계 맞추기 (ID : CLOCKSYNC)  (0) 2021.01.09
6.5 게임판 덮기(ID : BOARDCOVER)  (0) 2021.01.05
6.3 소풍  (0) 2021.01.03

6.8 시계 맞추기 (ID : CLOCKSYNC)

제출일 : 2021-01-09

문제 풀이 시간 : 1H


Link : https://algospot.com/judge/problem/read/CLOCKSYNC

문제

)

그림과 같이 4 x 4 개의 격자 형태로 배치된 16개의 시계가 있다. 이 시계들은 모두 12시, 3시, 6시, 혹은 9시를 가리키고 있다. 이 시계들이 모두 12시를 가리키도록 바꾸고 싶다.

시계의 시간을 조작하는 유일한 방법은 모두 10개 있는 스위치들을 조작하는 것으로, 각 스위치들은 모두 적게는 3개에서 많게는 5개의 시계에 연결되어 있다. 한 스위치를 누를 때마다, 해당 스위치와 연결된 시계들의 시간은 3시간씩 앞으로 움직인다. 스위치들과 그들이 연결된 시계들의 목록은 다음과 같다.

0 0, 1, 2
1 3, 7, 9, 11
2 4, 10, 14, 15
3 0, 4, 5, 6, 7
4 6, 7, 8, 10, 12
5 0, 2, 14, 15
6 3, 14, 15
7 4, 5, 7, 14, 15
8 1, 2, 3, 4, 5
9 3, 4, 5, 9, 13

시계들은 맨 윗줄부터, 왼쪽에서 오른쪽으로 순서대로 번호가 매겨졌다고 가정하자. 시계들이 현재 가리키는 시간들이 주어졌을 때, 모든 시계를 12시로 돌리기 위해 최소한 눌러야 할 스위치의 수를 계산하는 프로그램을 작성하시오.

입력의 첫 줄에는 테스트 케이스의 수 C (C <= 30) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 2개의 정수 H, W (1 <= H,W <= 20) 가 주어집니다. 다음 H 줄에 각 W 글자로 게임판의 모양이 주어집니다. # 은 검은 칸, . 는 흰 칸을 나타냅니다. 입력에 주어지는 게임판에 있는 흰 칸의 수는 50 을 넘지 않습니다.

입력

첫 줄에 테스트 케이스의 개수 C (<= 30) 가 주어진다.
각 테스트 케이스는 한 줄에 16개의 정수로 주어지며, 각 정수는 0번부터 15번까지 각 시계가 가리키고 있는 시간을 12, 3, 6, 9 중 하나로 표현한다.

출력

각 테스트 케이스당 한 줄을 출력한다. 시계들을 모두 12시로 돌려놓기 위해 눌러야 할 스위치의 최소 수를 출력한다. 만약 이것이 불가능할 경우 -1 을 출력한다.

예제

입력

2
12 6 6 6 6 6 12 12 12 12 12 12 12 12 12 12 
12 9 3 12 6 6 9 3 12 9 12 9 12 12 6 6

출력

2
9

문제 풀이 접근

이 문제를 해결하기 위한 중요 아이디어는 같은 스위치를 4번누를 경우와 0번 누르는 경우가 동일하다는것이다. 다시 말하자면 4번 이상 같은 스위치를 누를 필요가 없다는 것을 의미한다.

따라서 위 문제는 4^10가지의 경우의 수를 탐색하여 문제를 해결 할 수 있기에 모든 경우의 수를 탐색하더라도 제한시간내에 문제를 해결할 수 있다.

따라서 재귀함수를 이용하여 문제를 해결 했다.

코드

언어 : JAVA

실행 시간 : 1340ms

import java.util.Scanner;

public class Main {
    static int INF = Integer.MAX_VALUE >> 1, SWITCH = 10, CLOCK = 16;
    static int[][] switches = { { 0, 1, 2 }, { 3, 7, 9, 11 }, { 4, 10, 14, 15 }, { 0, 4, 5, 6, 7 }, { 6, 7, 8, 10, 12 },
            { 0, 2, 14, 15 }, { 3, 14, 15 }, { 4, 5, 7, 14, 15 }, { 1, 2, 3, 4, 5 }, { 3, 4, 5, 9, 13 } };
    static int[] clocks;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int C = sc.nextInt();
        for (int tc = 0; tc < C; tc++) {
            clocks = new int[CLOCK];
            for (int clock = 0; clock < CLOCK; clock++)
                clocks[clock] = (sc.nextInt() / 3) % 4;

            int ret = solve(0);
            System.out.println(ret != INF ? ret : -1);
        }
    }

    private static boolean check() {
        for (int i : clocks)
            if (i != 0)
                return false;
        return true;
    }

    private static void push(int depth) {
        for (int i : switches[depth]) {
            clocks[i]++;
            clocks[i] %= 4;
        }
    }

    private static int solve(int depth) {
        if (depth == SWITCH) {
            return check() ? 0 : INF;
        }

        int ret = INF;
        for (int cnt = 0; cnt < 4; cnt++) {
            ret = Math.min(ret, cnt + solve(depth + 1));
            push(depth);
        }
        return ret;
    }
}

'Problem' 카테고리의 다른 글

7.2 쿼드 트리 뒤집기(ID : QUADTREE)  (0) 2021.01.12
6.5 게임판 덮기(ID : BOARDCOVER)  (0) 2021.01.05
6.3 소풍  (0) 2021.01.03

6.5 게임판 덮기(ID : BOARDCOVER)

제출일 : 2021-01-05

문제 풀이 시간 : 1H


Link : https://www.algospot.com/judge/problem/read/BOARDCOVER

문제

03.png

H*W 크기의 게임판이 있습니다. 게임판은 검은 칸과 흰 칸으로 구성된 격자 모양을 하고 있는데 이 중 모든 흰 칸을 3칸짜리 L자 모양의 블록으로 덮고 싶습니다. 이 때 블록들은 자유롭게 회전해서 놓을 수 있지만, 서로 겹치거나, 검은 칸을 덮거나, 게임판 밖으로 나가서는 안 됩니다. 위 그림은 한 게임판과 이를 덮는 방법을 보여줍니다.

게임판이 주어질 때 이를 덮는 방법의 수를 계산하는 프로그램을 작성하세요.

입력

입력의 첫 줄에는 테스트 케이스의 수 C (C <= 30) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 2개의 정수 H, W (1 <= H,W <= 20) 가 주어집니다. 다음 H 줄에 각 W 글자로 게임판의 모양이 주어집니다. # 은 검은 칸, . 는 흰 칸을 나타냅니다. 입력에 주어지는 게임판에 있는 흰 칸의 수는 50 을 넘지 않습니다.

출력

한 줄에 하나씩 흰 칸을 모두 덮는 방법의 수를 출력합니다.

예제

입력

3 
3 7 
#.....# 
#.....# 
##...## 
3 7 
#.....# 
#.....# 
##..### 
8 10 
########## 
#........# 
#........# 
#........# 
#........# 
#........# 
#........# 
########## 

출력

0
2
1514

문제 풀이 접근

가능한 경우의 수를 재귀함수를 이용하여 구현하였습니다.

예전 N-QUEEN 문제를 풀어봐서 그런지 쉽게 풀수 있었습니다.

왼쪽 위가 0,0이 되도록 하여 블록의 4가지 방향의 상대위치 값을 미리 저장하고. 배열에서 빈 공간을 만났을때 4가지 블록을 비교하여 해당 칸에 블록을 둘 수 있는경우 재귀함수를 호출하도록 문제를 해결하였습니다.

각 메소드명을 기준으로 간단하게 설명을 하면

  • Solve()는 재귀의 메인이 되는 메소드입니다.

  • check(k,i,j)는 현재 위치(i,j)가 빈칸.일 경우에 호출되는 함수로 k번째 블록이 들어갈 수 있는지 확인하는 메소드입니다.

  • isRange(nr,nc)는 (nr, nc)의 값이 보드를 벗어나는지 확인하는 메소드입니다.

  • set(k, i, j, v)는 (i, j)를 기준으로 K번째 블록의 위치 값을 v로 변경하는 메소드입니다. 이 함수를 통해 백트래킹을 쉽게 구현할 수 있습니다.

코드

언어 : JAVA

실행 시간 : 180ms

import java.util.Scanner;

public class Main {
    static int H, W, ans;
    static int[][] map;
    static int[][][] block = { { { 0, 0 }, { 0, 1 }, { 1, 1 } }, { { 0, 0 }, { 0, 1 }, { 1, 0 } },
                              { { 0, 0 }, { 1, 0 }, { 1, 1 } }, { { 0, 0 }, { 1, 0 }, { 1, -1 } }, };

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int C = sc.nextInt();
        for (int tc = 0; tc < C; tc++) {
            H = sc.nextInt();
            W = sc.nextInt();

            map = new int[H][W];
            int cnt = 0;
            for (int i = 0; i < H; i++) {
                String input = sc.next();
                for (int j = 0; j < W; j++) {
                    map[i][j] = input.charAt(j) == '.' ? 0 : 1;
                    if (map[i][j] == 0)
                        cnt++;
                }
            }

            if (cnt % 3 != 0) {
                System.out.println(0);
            } else {
                ans = 0;
                solve(cnt);
                System.out.println(ans);
            }
        }
    }

    private static void solve(int cnt) {
        if (cnt == 0) {
            ans++;
            return;
        }

        for (int i = 0; i < H; i++) {
            for (int j = 0; j < W; j++) {
                if (map[i][j] == 0) {
                    for (int k = 0; k < 4; k++) {
                        if (check(k, i, j)) {
                            set(k, i, j, 1);
                            solve(cnt - 3);
                            set(k, i, j, 0);
                        }
                    }
                    return;
                }
            }
        }

    }

    private static void set(int k, int r, int c, int v) {
        for (int i = 0; i < 3; i++) {
            int nr = r + block[k][i][0];
            int nc = c + block[k][i][1];
            map[nr][nc] = v;
        }
    }

    private static boolean check(int k, int r, int c) {
        for (int i = 0; i < 3; i++) {
            int nr = r + block[k][i][0];
            int nc = c + block[k][i][1];
            if (!isRange(nr, nc) || map[nr][nc] == 1) {
                return false;
            }
        }
        return true;
    }

    private static boolean isRange(int nr, int nc) {
        if (0 <= nr && nr < H && 0 <= nc && nc < W)
            return true;
        return false;
    }
}

'Problem' 카테고리의 다른 글

7.2 쿼드 트리 뒤집기(ID : QUADTREE)  (0) 2021.01.12
6.8 시계 맞추기 (ID : CLOCKSYNC)  (0) 2021.01.09
6.3 소풍  (0) 2021.01.03

6.3 소풍

제출일 : 2021-01-03

문제 풀이 시간 : 30M


Link : https://algospot.com/judge/problem/read/PICNIC

문제

안드로메다 유치원 익스프레스반에서는 다음 주에 율동공원으로 소풍을 갑니다. 원석 선생님은 소풍 때 학생들을 두 명씩 짝을 지어 행동하게 하려고 합니다. 그런데 서로 친구가 아닌 학생들끼리 짝을 지어 주면 서로 싸우거나 같이 돌아다니지 않기 때문에, 항상 서로 친구인 학생들끼리만 짝을 지어 줘야 합니다.

각 학생들의 쌍에 대해 이들이 서로 친구인지 여부가 주어질 때, 학생들을 짝지어줄 수 있는 방법의 수를 계산하는 프로그램을 작성하세요. 짝이 되는 학생들이 일부만 다르더라도 다른 방법이라고 봅니다. 예를 들어 다음 두 가지 방법은 서로 다른 방법입니다.

  • (태연,제시카) (써니,티파니) (효연,유리)
  • (태연,제시카) (써니,유리) (효연,티파니)

입력

입력의 첫 줄에는 테스트 케이스의 수 C (C <= 50) 가 주어집니다. 각 테스트 케이스의 첫 줄에는 학생의 수 n (2 <= n <= 10) 과 친구 쌍의 수 m (0 <= m <= n*(n-1)/2) 이 주어집니다. 그 다음 줄에 m 개의 정수 쌍으로 서로 친구인 두 학생의 번호가 주어집니다. 번호는 모두 0 부터 n-1 사이의 정수이고, 같은 쌍은 입력에 두 번 주어지지 않습니다. 학생들의 수는 짝수입니다.

출력

각 테스트 케이스마다 한 줄에 모든 학생을 친구끼리만 짝지어줄 수 있는 방법의 수를 출력합니다.

예제

입력

3 
2 1 
0 1 
4 6 
0 1 1 2 2 3 3 0 0 2 1 3 
6 10 
0 1 0 2 1 2 1 3 1 4 2 3 2 4 3 4 3 5 4 5

출력

1
3
4

문제 풀이 접근

가능한 경우의 수를 재귀함수를 이용하여 구현하였습니다.

A라는 변수를 통해 짝이 맞춰지지 않은 가장 빠른 번호의 학생을 구하고 A번 학생과 짝이 가능하다면 짝을 지어주고 재귀함수를 호출하여 문제를 해결하였습니다.

처음 문제를 읽고 가능한 모든 경우의 수를 파악한 뒤 요구조건에 만족한다면 카운트 하는 방식으로 문제를 해결하려 했습니다. 아래의 소스코드보다 더 많은 변수들이 필요했고 중복을 제거하기 위해 같은 경우지만 다른 경우로 카운트 (1122 == 2211)되는 경우를 제거 해주어야 했습니다. 소스코드가 점점 길어지고 지저분해져 아래와 같이 가능한 경우만 계속 재귀함수를 호출할 수 있도록 코딩하였습니다.

코드

언어 : JAVA

실행 시간 : 176ms

import java.util.Scanner;

public class Main {
    static int N, M, ans;
    static boolean[] visit;
    static int[][] map;

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

        int T = sc.nextInt();
        for (int tc = 0; tc < T; tc++) {
            N = sc.nextInt();
            M = sc.nextInt();

            map = new int[N][N];
            for (int i = 0; i < M; i++) {
                int a = sc.nextInt();
                int b = sc.nextInt();
                map[a][b] = map[b][a] = 1;
            }

            ans = 0;
            visit = new boolean[N];
            solve();
            System.out.println(ans);
        }
    }

    private static void solve() {
        int A = -1;
        for (int i = 0; i < N; i++) {
            if (!visit[i]) {
                A = i;
                break;
            }
        }

        if (A == -1) {
            ans++;
            return;
        }

        for (int i = A + 1; i < N; i++) {
            if (!visit[i] && map[A][i] == 1) {
                visit[A] = visit[i] = true;
                solve();
                visit[A] = visit[i] = false;
            }
        }
    }

}

'Problem' 카테고리의 다른 글

7.2 쿼드 트리 뒤집기(ID : QUADTREE)  (0) 2021.01.12
6.8 시계 맞추기 (ID : CLOCKSYNC)  (0) 2021.01.09
6.5 게임판 덮기(ID : BOARDCOVER)  (0) 2021.01.05

[2018 KAKAO] 셔틀버스

제출일 : 2020-08-29

문제 풀이 시간 : 2H 30M


link : https://programmers.co.kr/learn/courses/30/lessons/17678

문제 설명

카카오에서는 무료 셔틀버스를 운행하기 때문에 판교역에서 편하게 사무실로 올 수 있다. 카카오의 직원은 서로를 '크루'라고 부르는데, 아침마다 많은 크루들이 이 셔틀을 이용하여 출근한다.

이 문제에서는 편의를 위해 셔틀은 다음과 같은 규칙으로 운행한다고 가정하자.

  • 셔틀은 09:00부터 총 nt분 간격으로 역에 도착하며, 하나의 셔틀에는 최대 m명의 승객이 탈 수 있다.
  • 셔틀은 도착했을 때 도착한 순간에 대기열에 선 크루까지 포함해서 대기 순서대로 태우고 바로 출발한다. 예를 들어 09:00에 도착한 셔틀은 자리가 있다면 09:00에 줄을 선 크루도 탈 수 있다.

일찍 나와서 셔틀을 기다리는 것이 귀찮았던 콘은, 일주일간의 집요한 관찰 끝에 어떤 크루가 몇 시에 셔틀 대기열에 도착하는지 알아냈다. 콘이 셔틀을 타고 사무실로 갈 수 있는 도착 시각 중 제일 늦은 시각을 구하여라.

단, 콘은 게으르기 때문에 같은 시각에 도착한 크루 중 대기열에서 제일 뒤에 선다. 또한, 모든 크루는 잠을 자야 하므로 23:59에 집에 돌아간다. 따라서 어떤 크루도 다음날 셔틀을 타는 일은 없다.

입력 형식

셔틀 운행 횟수 n, 셔틀 운행 간격 t, 한 셔틀에 탈 수 있는 최대 크루 수 m, 크루가 대기열에 도착하는 시각을 모은 배열 timetable이 입력으로 주어진다.

  • 0 < n ≦ 10
  • 0 < t ≦ 60
  • 0 < m ≦ 45
  • timetable은 최소 길이 1이고 최대 길이 2000인 배열로, 하루 동안 크루가 대기열에 도착하는 시각이 HH:MM 형식으로 이루어져 있다.
  • 크루의 도착 시각 HH:MM00:01에서 23:59 사이이다.

출력 형식

콘이 무사히 셔틀을 타고 사무실로 갈 수 있는 제일 늦은 도착 시각을 출력한다. 도착 시각은 HH:MM 형식이며, 00:00에서 23:59 사이의 값이 될 수 있다.

입출력 예제

n t m timetable answer
1 1 5 [08:00, 08:01, 08:02, 08:03] 09:00
2 10 2 [09:10, 09:09, 08:00] 09:09
2 1 2 [09:00, 09:00, 09:00, 09:00] 08:59
1 1 5 [00:01, 00:01, 00:01, 00:01, 00:01] 00:00
1 1 1 [23:59] 09:00
10 60 45 [23:59,23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59, 23:59] 18:00

문제 풀이 접근

문제를 풀며 꽤나 시간이 많이 걸렸습니다.

처음에 작성한 코드는 주어진 테스트 케이스는 맞지만 체점결과 1개의 케이스가 실패하여 처음부터 코드를 작성하였습니다.

도착시간을 우선순위 큐에 담고 버스가 도착하는 시간에 탑승하는 인원을 확인하고 현재 도착한 셔틀버스를 타기 위한 가장 늦은 시간을 구하는 방식으로 문제를 접근하여 풀었습니다. 22번 테스트케이스가 어떠한 것인지 자꾸 실패하였고 예외를 계속 코딩하다보니 코드가 너무 지저분해지기만 해 코드를 버리고 새로 작성하였습니다.

저는 우선순위큐를 사용했지만 다른 사람들의 풀이를 보니 대부분 String배열을 정렬하여 문제를 해결한것을 보고 나는 왜 이렇게 간단하게 생각하지 못했나 하는 생각을 했습니다.

아래의 코드는 모든 시간에 셔틀버스를 탄 사람들을 각각 다 저장하고 뒤에서부터 탐색하여 빈자리가 있다면 셔틀버스가 오는 시간이 가장 늦은 시간이 되고 빈자리가 없다면 앞사람보다 1분 빨리 도착하는 경우를 구했습니다.

코드

언어 : JAVA

package Programmers.kakao;

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class kakao2018_4_셔틀버스 {
    public static void main(String[] args) {
        System.out.println(solution(1, 1, 5, new String[] { "08:00", "08:01", "08:02", "08:03" }));
        System.out.println(solution(2, 10, 2, new String[] { "09:10", "09:09", "08:00" }));
        System.out.println(solution(2, 1, 2, new String[] { "09:00", "09:00", "09:00", "09:00" }));
        System.out.println(solution(1, 1, 5, new String[] { "00:01", "00:01", "00:01", "00:01", "00:01" }));
        System.out.println(solution(1, 1, 1, new String[] { "23:59" }));
        System.out.println(solution(10, 60, 5, new String[] { "23:59", "23:59", "23:59", "23:59", "23:59", "23:59",    "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59" }));
    }

    public static String solution(int n, int t, int m, String[] timetable) {

        PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[0] == o2[0])
                    return o1[1] - o2[1];
                return o1[0] - o2[0];
            }
        });
        for (int i = 0; i < timetable.length; i++) {
            StringTokenizer st = new StringTokenizer(timetable[i], ":");
            pq.add(new int[] { Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()) });
        }

        int H = 9, M = 0;
        int[][][] bus = new int[n][m][2];
        int[][] time = new int[n][2];
        for (int k = 0; k < n; k++) {
            time[k][0] = H;
            time[k][1] = M;
            int cnt = 0;
            for (int i = 0; i < m; i++) {
                bus[k][i][0] = -1;
            }
            while (cnt < m && !pq.isEmpty()) {
                int[] now = pq.peek();
                if (now[0] < H || (now[0] == H && now[1] <= M)) {
                    bus[k][cnt] = pq.poll();
                    cnt++;
                } else
                    break;
            }
            // 시간 증가
            H = (M + t >= 60) ? H + 1 : H;
            M = (M + t >= 60) ? M + t - 60 : M + t;
        }

        for (int k = n - 1; k >= 0; k--) {
            for (int i = m - 1; i >= 0; i--) {
                if (bus[k][i][0] == -1)
                    return String.format("%02d:%02d", time[k][0], time[k][1]);

                if (i > 0 && bus[k][i - 1][0] == bus[k][i][0] && bus[k][i - 1][1] == bus[k][i][1])
                    continue;

                if (bus[k][i][1] - 1 < 0)
                    return String.format("%02d:%02d", bus[k][i][0] - 1, 59);
                else
                    return String.format("%02d:%02d", bus[k][i][0], bus[k][i][1] - 1);

            }
        }
        return "";
    }
}

'Problem > Programmers' 카테고리의 다른 글

[2018 KAKAO] 캐시  (0) 2020.08.29
[2018 KAKAO] 다트게임  (0) 2020.08.29
[2018 KAKAO] 비밀지도  (0) 2020.08.29
[카카오 2020 인턴쉽] - 키패드 누르기(JAVA)  (0) 2020.07.27
[Programmers] K번째수 - (Java)  (0) 2020.05.15

[2018 KAKAO] 캐시

제출일 : 2020-08-28

문제 풀이 시간 : 15M


link : https://programmers.co.kr/learn/courses/30/lessons/17680

문제 설명

지도개발팀에서 근무하는 제이지는 지도에서 도시 이름을 검색하면 해당 도시와 관련된 맛집 게시물들을 데이터베이스에서 읽어 보여주는 서비스를 개발하고 있다.
이 프로그램의 테스팅 업무를 담당하고 있는 어피치는 서비스를 오픈하기 전 각 로직에 대한 성능 측정을 수행하였는데, 제이지가 작성한 부분 중 데이터베이스에서 게시물을 가져오는 부분의 실행시간이 너무 오래 걸린다는 것을 알게 되었다.
어피치는 제이지에게 해당 로직을 개선하라고 닦달하기 시작하였고, 제이지는 DB 캐시를 적용하여 성능 개선을 시도하고 있지만 캐시 크기를 얼마로 해야 효율적인지 몰라 난감한 상황이다.

어피치에게 시달리는 제이지를 도와, DB 캐시를 적용할 때 캐시 크기에 따른 실행시간 측정 프로그램을 작성하시오.

입력 형식

  • 캐시 크기(cacheSize)와 도시이름 배열(cities)을 입력받는다.
  • cacheSize는 정수이며, 범위는 0 ≦ cacheSize ≦ 30 이다.
  • cities는 도시 이름으로 이뤄진 문자열 배열로, 최대 도시 수는 100,000개이다.
  • 각 도시 이름은 공백, 숫자, 특수문자 등이 없는 영문자로 구성되며, 대소문자 구분을 하지 않는다. 도시 이름은 최대 20자로 이루어져 있다.

출력 형식

  • 입력된 도시이름 배열을 순서대로 처리할 때, 총 실행시간을 출력한다.

조건

  • 캐시 교체 알고리즘은 LRU(Least Recently Used)를 사용한다.
  • cache hit일 경우 실행시간은 1이다.
  • cache miss일 경우 실행시간은 5이다.

입출력 예제

캐시크기(cacheSize) 도시이름(cities) 실행시간
3 [Jeju, Pangyo, Seoul, NewYork, LA, Jeju, Pangyo, Seoul, NewYork, LA] 50
3 [Jeju, Pangyo, Seoul, Jeju, Pangyo, Seoul, Jeju, Pangyo, Seoul] 21
2 [Jeju, Pangyo, Seoul, NewYork, LA, SanFrancisco, Seoul, Rome, Paris, Jeju, NewYork, Rome] 60
5 [Jeju, Pangyo, Seoul, NewYork, LA, SanFrancisco, Seoul, Rome, Paris, Jeju, NewYork, Rome] 52
2 [Jeju, Pangyo, NewYork, newyork] 16
0 [Jeju, Pangyo, Seoul, NewYork, LA] 25

문제 풀이 접근

우선 LRU에 대한 개념이 있어야 문제를 해결할 수 있습니다.

여기서 간단하게 설명하자면 LRU는 페이지 교체 알고리즘으로 가장 오랫동안 사용되지 않은 페이지를 제거하는 알고리즘입니다.

저는 이를 큐를 사용하여 구현하였습니다.

최근에 사용되어 값이 큐에 저장되어 있는지 contain()을 이용하여 확인하였고 remove()를 이용하여 값을 제거하고 add()를 사용하여 큐에 가장 마지막 위치에 해당 값을 위치하도록 하였습니다.

이렇게 한다면 큐의 첫번째 값은 가장 오랫동안 사용하지 않은 값이 있게 되며 값이 존재하지 않을때 값을 추가하고 캐시의 크기보다 큐의 크기가 클경우 맨앞의 값을 꺼내왔습니다.

문제를 보면 대소문자를 구분하지 않는다고 명시되어 있습니다. 따라서 모든 도시의 이름을 소문자로 치환하여 문제를 해결하였습니다.

코드

언어 : JAVA

package Programmers.kakao;

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

public class kakao2018_3_캐시 {
    public static void main(String[] args) {
        System.out.println(solution(3, new String[] { "Jeju", "Pangyo", "Seoul", "NewYork", "LA", "Jeju", "Pangyo",
                "Seoul", "NewYork", "LA" }));
        System.out.println(solution(3,
                new String[] { "Jeju", "Pangyo", "Seoul", "Jeju", "Pangyo", "Seoul", "Jeju", "Pangyo", "Seoul" }));
        System.out.println(solution(2, new String[] { "Jeju", "Pangyo", "Seoul", "NewYork", "LA", "SanFrancisco",
                "Seoul", "Rome", "Paris", "Jeju", "NewYork", "Rome" }));
        System.out.println(solution(5, new String[] { "Jeju", "Pangyo", "Seoul", "NewYork", "LA", "SanFrancisco",
                "Seoul", "Rome", "Paris", "Jeju", "NewYork", "Rome" }));
        System.out.println(solution(2, new String[] { "Jeju", "Pangyo", "NewYork", "newyork" }));
        System.out.println(solution(0, new String[] { "Jeju", "Pangyo", "Seoul", "NewYork", "LA" }));
    }

    public static int solution(int cacheSize, String[] cities) {
        int answer = 0;
        Queue<String> q = new LinkedList<>();

        for (int i = 0; i < cities.length; i++) {
            cities[i] = cities[i].toLowerCase();
        }
        for (int i = 0; i < cities.length; i++) {
            if (q.contains(cities[i])) {
                q.remove(cities[i]);
                q.add(cities[i]);
                answer++;
            } else {
                q.add(cities[i]);
                if (q.size() > cacheSize) {
                    q.poll();
                }
                answer += 5;
            }
        }
        return answer;
    }
}

'Problem > Programmers' 카테고리의 다른 글

[2018 KAKAO] 셔틀버스  (0) 2020.08.30
[2018 KAKAO] 다트게임  (0) 2020.08.29
[2018 KAKAO] 비밀지도  (0) 2020.08.29
[카카오 2020 인턴쉽] - 키패드 누르기(JAVA)  (0) 2020.07.27
[Programmers] K번째수 - (Java)  (0) 2020.05.15

[2018 KAKAO] 다트게임

제출일 : 2020-08-28

문제 풀이 시간 : 30M


link : https://programmers.co.kr/learn/courses/30/lessons/17682

문제 설명

카카오톡에 뜬 네 번째 별! 심심할 땐? 카카오톡 게임별~

Game Star

카카오톡 게임별의 하반기 신규 서비스로 다트 게임을 출시하기로 했다. 다트 게임은 다트판에 다트를 세 차례 던져 그 점수의 합계로 실력을 겨루는 게임으로, 모두가 간단히 즐길 수 있다.
갓 입사한 무지는 코딩 실력을 인정받아 게임의 핵심 부분인 점수 계산 로직을 맡게 되었다. 다트 게임의 점수 계산 로직은 아래와 같다.

  1. 다트 게임은 총 3번의 기회로 구성된다.
  2. 각 기회마다 얻을 수 있는 점수는 0점에서 10점까지이다.
  3. 점수와 함께 Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수1 , 점수2 , 점수3 )으로 계산된다.
  4. 옵션으로 스타상(*) , 아차상(#)이 존재하며 스타상(*) 당첨 시 해당 점수와 바로 전에 얻은 점수를 각 2배로 만든다. 아차상(#) 당첨 시 해당 점수는 마이너스된다.
  5. 스타상(*)은 첫 번째 기회에서도 나올 수 있다. 이 경우 첫 번째 스타상(*)의 점수만 2배가 된다. (예제 4번 참고)
  6. 스타상(*)의 효과는 다른 스타상(*)의 효과와 중첩될 수 있다. 이 경우 중첩된 스타상(*) 점수는 4배가 된다. (예제 4번 참고)
  7. 스타상(*)의 효과는 아차상(#)의 효과와 중첩될 수 있다. 이 경우 중첩된 아차상(#)의 점수는 -2배가 된다. (예제 5번 참고)
  8. Single(S), Double(D), Triple(T)은 점수마다 하나씩 존재한다.
  9. 스타상(*), 아차상(#)은 점수마다 둘 중 하나만 존재할 수 있으며, 존재하지 않을 수도 있다.

0~10의 정수와 문자 S, D, T, *, #로 구성된 문자열이 입력될 시 총점수를 반환하는 함수를 작성하라.

입력 형식

점수|보너스|[옵션]으로 이루어진 문자열 3세트.
예) 1S2D*3T

  • 점수는 0에서 10 사이의 정수이다.
  • 보너스는 S, D, T 중 하나이다.
  • 옵선은 *이나 # 중 하나이며, 없을 수도 있다.

출력 형식

3번의 기회에서 얻은 점수 합계에 해당하는 정수값을 출력한다.
예) 37

입출력 예제

예제 dartResult answer 설명
1 1S2D*3T 37 11 * 2 + 22 * 2 + 33
2 1D2S#10S 9 12 + 21 * (-1) + 101
3 1D2S0T 3 12 + 21 + 03
4 1S*2T*3S 23 11 * 2 * 2 + 23 * 2 + 31
5 1D#2S*3S 5 12 * (-1) * 2 + 21 * 2 + 31
6 1T2D3D# -4 13 + 22 + 32 * (-1)
7 1D2S3T* 59 12 + 21 * 2 + 33 * 2

문제 풀이 접근

입력으로 주어진 문자열을 점수|보너스|[옵션]의 형식으로 쪼개기만 하면 쉽게 문제를 해결할 수 있습니다.

저는 3개의 세트가 입력으로 들어오기 때문에 for문을 이용하여 3번 반복을 하였고 i값을 인덱스로 사용하였습니다.

문제를 풀며 주의해야하는 부분은 점수가 0~10점까지 주어지기 때문에 2개의 자리수가 점수일 경우를 고려해야 합니다.

코드

언어 : JAVA

package Programmers.kakao;
import java.util.Arrays;

public class kakao2018_2_다트게임 {
    public static void main(String[] args) {
        System.out.println(solution("1S2D*3T")); // 37
        System.out.println(solution("1D2S#10S")); // 9
        System.out.println(solution("1D2S0T")); // 3
        System.out.println(solution("1S*2T*3S")); // 23
        System.out.println(solution("1D#2S*3S")); // 5
        System.out.println(solution("1T2D3D#")); // -4
        System.out.println(solution("1D2S3T*")); // 59
    }

    public static int solution(String dartResult) {
        int[] score = new int[3];
        char[] bonus = new char[3];
        char[] option = new char[3];

        int i = 0;
        for (int k = 0; k < 3; k++) {
            // 각 기회마다 얻을 수 있는 점수는 0~10
            score[k] = dartResult.charAt(i++) - '0';
            if (dartResult.charAt(i) == '0') {
                score[k] *= 10;
                i++;
            }

            // 보너스는 SDT 중 하나
            bonus[k] = dartResult.charAt(i++);

            // 옵선은 *이나 # 중 하나이며, 없을 수도 있다.
            if (i < dartResult.length() && (dartResult.charAt(i) == '*' || dartResult.charAt(i) == '#')) {
                option[k] = dartResult.charAt(i++);
            }
        }

        for (int k = 0; k < 3; k++) {
            if (bonus[k] == 'D')
                score[k] = score[k] * score[k];
            else if (bonus[k] == 'T')
                score[k] = score[k] * score[k] * score[k];

            if (option[k] == '*') {
                score[k] *= 2;
                if (k > 0)
                    score[k - 1] *= 2;
            } else if (option[k] == '#') {
                score[k] *= -1;
            }
        }
        return score[0] + score[1] + score[2];
    }
}

'Problem > Programmers' 카테고리의 다른 글

[2018 KAKAO] 셔틀버스  (0) 2020.08.30
[2018 KAKAO] 캐시  (0) 2020.08.29
[2018 KAKAO] 비밀지도  (0) 2020.08.29
[카카오 2020 인턴쉽] - 키패드 누르기(JAVA)  (0) 2020.07.27
[Programmers] K번째수 - (Java)  (0) 2020.05.15

[2018 KAKAO] 비밀지도

제출일 : 2020-08-28

문제 풀이 시간 : 30M


link : https://programmers.co.kr/learn/courses/30/lessons/17681

문제 설명

네오는 평소 프로도가 비상금을 숨겨놓는 장소를 알려줄 비밀지도를 손에 넣었다. 그런데 이 비밀지도는 숫자로 암호화되어 있어 위치를 확인하기 위해서는 암호를 해독해야 한다. 다행히 지도 암호를 해독할 방법을 적어놓은 메모도 함께 발견했다.

  1. 지도는 한 변의 길이가 n인 정사각형 배열 형태로, 각 칸은 공백(" ) 또는벽(#") 두 종류로 이루어져 있다.
  2. 전체 지도는 두 장의 지도를 겹쳐서 얻을 수 있다. 각각 지도 1과 지도 2라고 하자. 지도 1 또는 지도 2 중 어느 하나라도 벽인 부분은 전체 지도에서도 벽이다. 지도 1과 지도 2에서 모두 공백인 부분은 전체 지도에서도 공백이다.
  3. 지도 1과 지도 2는 각각 정수 배열로 암호화되어 있다.
  4. 암호화된 배열은 지도의 각 가로줄에서 벽 부분을 1, 공백 부분을 0으로 부호화했을 때 얻어지는 이진수에 해당하는 값의 배열이다.

secret map

네오가 프로도의 비상금을 손에 넣을 수 있도록, 비밀지도의 암호를 해독하는 작업을 도와줄 프로그램을 작성하라.

입력 형식

입력으로 지도의 한 변 크기 n 과 2개의 정수 배열 arr1, arr2가 들어온다.

  • 1 ≦ n ≦ 16
  • arr1, arr2는 길이 n인 정수 배열로 주어진다.
  • 정수 배열의 각 원소 x를 이진수로 변환했을 때의 길이는 n 이하이다. 즉, 0 ≦ x ≦ 2n - 1을 만족한다.

출력 형식

원래의 비밀지도를 해독하여 '#', 공백으로 구성된 문자열 배열로 출력하라.

입출력 예제

매개변수
n 5
arr1 [9, 20, 28, 18, 11]
arr2 [30, 1, 21, 17, 28]
출력 ["#####","# # #", "### #", "# ##", "#####"]
매개변수
n 6
arr1 [46, 33, 33 ,22, 31, 50]
arr2 [27 ,56, 19, 14, 14, 10]
출력 ["######", "### #", "## ##", " #### ", " #####", "### # "]

문제 풀이 접근

주어진 배열을 비트연산하고 값을 2진수로 변환하여 1을 #으로 0을 공백으로 변환하여 값을 리턴하여 문제를 해결하였습니다.

반환해야 하는 String 배열을 생성하고 입력받은 배열을 OR(|)연산을 수행한 후에 Integer.toBinaryString()를 이용아여 2진수 형식으로 바꾸어 주었습니다. 또 변환된 값의 길이가 n보다 작을 수 있기 때문에 String.format()를 이용하여 자리수를 고정하고 replaceAll()을 이용하여 문자를 치환하여 문제를 해결하였습니다.

비트연산으로 문제를 해결해야 한다는 접근은 어렵지 않았으나 Integer.toBinaryString()String.fomat(), replaceAll()등 사용빈도가 적은 함수들을 사용해 보았다면 더욱 쉽게 문제를 해결할 수 있을 것이라 생각합니다.

코드

언어 : JAVA

package Programmers.kakao;
import java.util.Arrays;

public class kakao2018_1_비밀지도 {
    public static void main(String[] args) {
        System.out.println(
                Arrays.toString(solution(5, new int[] { 9, 20, 28, 18, 11 }, new int[] { 30, 1, 21, 17, 28 })));
        System.out.println(Arrays
                .toString(solution(6, new int[] { 46, 33, 33, 22, 31, 50 }, new int[] { 27, 56, 19, 14, 14, 10 })));
    }

    public static String[] solution(int n, int[] arr1, int[] arr2) {
        String[] result = new String[n];
        for (int i = 0; i < n; i++) {
            result[i] = Integer.toBinaryString(arr1[i] | arr2[i]);
        }

        for (int i = 0; i < n; i++) {
            result[i] = String.format("%" + n + "s", result[i]);
            result[i] = result[i].replaceAll("1", "#");
            result[i] = result[i].replaceAll("0", " ");
        }

        return result;
    }
}

'Problem > Programmers' 카테고리의 다른 글

[2018 KAKAO] 캐시  (0) 2020.08.29
[2018 KAKAO] 다트게임  (0) 2020.08.29
[카카오 2020 인턴쉽] - 키패드 누르기(JAVA)  (0) 2020.07.27
[Programmers] K번째수 - (Java)  (0) 2020.05.15
[Programmers] 체육복 - (Java)  (0) 2020.05.15