문제

https://school.programmers.co.kr/learn/courses/30/lessons/42579?language=java

풀이

  1. 속한 노래가 많이 재생된 장르를 먼저 수록합니다.
  2. 장르 내에서 많이 재생된 노래를 먼저 수록합니다.
  3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다.

• 장르에 속한 곡이 하나라면, 하나의 곡만 선택합니다.

이 조건들을 위주로 생각하여 문제를 풀었다.

우선, 장르, 재생횟수, 인덱스를 저장하기 위해 Triple이라는 자료구조를 하나 만들어줬다.

또한, 장르별 재생횟수를 저장하기 위해 Pair 자료구조와 함께 HashMap을 사용했다.

Triple 자료구조를 저장하는 list에 모든 데이터를 담고, 이 후 재생횟수 순서대로 Triple을 정렬시켜줬다.

이후 이중 for문을 통해 장르별 최대 두개의 곡을 선택한다. 횟수가 2회가 되면 반복문을 종료하게 했다. 이로써 장르에 곡이 1개인 경우 해당 인덱스를 하나만 추가하게 된다.

list 자료구조에 담은 데이터를 배열로 변환하여 리턴하면 끝

코드

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

class Solution {
         class Pair<T,V> {
        T first;
        V second;

        public Pair(T first, V second) {
            this.first = first;
            this.second = second;
        }
    }
    class Triple<T,K,V> {
        T first;
        K second;
        V third;

        public Triple(T first, K second, V third) {
            this.first = first;
            this.second = second;
            this.third = third;
        }
    }
    public int[] solution(String[] genres, int[] plays) {
        int[] answer = {};
        HashMap<String, Integer> map = new HashMap<>();
        ArrayList<Triple<Integer, String, Integer>> list = new ArrayList<>();
        ArrayList<Pair<String, Integer>> arrayList = new ArrayList<>();
        ArrayList<Integer> ans = new ArrayList<>();
        for (int i = 0; i < genres.length; i++) {
            map.put(genres[i], map.getOrDefault(genres[i], 0) + plays[i]);
            list.add(new Triple(i, genres[i], plays[i]));
        }
        map.forEach((s, i) -> {
            arrayList.add(new Pair(s,i));
        });
        Collections.sort(list, new Comparator<Triple<Integer, String, Integer>>() {
            @Override
            public int compare(Triple<Integer, String, Integer> o1,
                Triple<Integer, String, Integer> o2) {
                return o2.third - o1.third;
            }
        });

        Collections.sort(arrayList, new Comparator<Pair<String, Integer>>() {
            @Override
            public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
                return o2.second-o1.second;
            }
        });
        int idx = 0;
        for (int i = 0; i < arrayList.size(); i++) {
            String s = arrayList.get(i).first;
            int cnt = 0;
            for (int j = 0; j < list.size(); j++) {
                if (cnt == 2) break;
                if (list.get(j).second.equals(s)) {
                    ans.add(list.get(j).first);
                    cnt++;
                }
            }
        }
        answer = new int[ans.size()];
        for (int i = 0; i < ans.size(); i++) {
            answer[i] = ans.get(i);
        }
        return answer;
    }
}

문제

https://school.programmers.co.kr/learn/courses/30/lessons/1844?language=java

풀이

틀린 풀이

DFS를 통해 문제를 먼저 풀었지만 효율성 테스트에서 모두 실패했다.

새로운 풀이

문제점

DFS는 다음과 같은 문제가 있기 때문에 이 문제를 해결하는 방법으로 적합하지 않다.

  • 해가 없는 경로가 깊을 경우 탐색시간이 오래 걸릴 수 있다.
  • 얻어진 해가 최단 경로가 된다는 보장이 없다.

따라서, BFS로 다시 문제를 풀었다.

또한 맨날 헷갈리는 부분이 다익스트라(PQ)를 사용할 것인지 인데, PQ의 경우 가중치가 다른 경우에 사용한다는 것을 다시 기억하자.

일반적인 방법의 BFS를 통해 문제를 해결할 수 있다.

코드

package com.company.programmers;

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

public class P4 {
    static int[] dy = new int[] {-1,1,0,0};
    static int[] dx = new int[] {0,0,-1,1};
    static int[][] map;
    static boolean[][] visited;
    static int min = Integer.MAX_VALUE;
    public int solution(int[][] maps) {
        map = maps;

        visited = new boolean[maps.length][maps[0].length];
        visited[0][0] = true;
        bfs();
        if (min == Integer.MAX_VALUE) min = -1;
        return min;
    }
    public static void bfs() {
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[]{0,0,1});
        while (!queue.isEmpty()) {
            int[] val = queue.poll();

            if (val[0] == map.length - 1  && val[1] == map[0].length - 1) {
                min = Math.min(val[2], min);
                return;
            }
            for (int i = 0; i < 4; i++) {
                int ny = val[0] + dy[i];
                int nx = val[1] + dx[i];

                if (ny < 0 || nx < 0 || ny >= map.length || nx >= map[0].length) continue;
                if (visited[ny][nx]) continue;
                if (map[ny][nx] == 0) continue;
                visited[ny][nx] = true;
                queue.add(new int[] {ny,nx,val[2]+1});
            }
        }

    }
    public static void main(String[] args) {
        P4 p4 = new P4();
        p4.solution(new int[][] {{1,0,1,1,1}, {1,0,1,0,1}, {1,0,1,1,1}, {1,1,1,0,1}, {0,0,0,0,1}});

    }

}

문제

https://school.programmers.co.kr/learn/courses/30/lessons/72411?language=java

풀이

소요시간 60분

틀린 풀이

조합을 통해 가능한 전체 코스의 경우의 수를 구하고, 코스가 등장한 갯수를 구하기 위해 map을 이용했다. 이후 map에서 2개 이상 등장 했으며, 코스의 길이가 orders 배열에 포함된 경우 추가하도록 했다.

문제점

코스에 만약 ABCD가 있다면, BCD, CD 와같이 ABCD 내에 포함된 경우를 제거하는 방법을 찾지 못해 풀지 못했다.

새로운 풀이

2개의 코스 요리중 가장 많이 주문한 경우, 3개의 코스 요리중 가장 많이 주문한 경우와 같이 각 코스종류마다 답을 구하도록 수정했다. 풀이를 보고나서 다시 문제를 보니, 이 부분이 중요했다는 사실을 알 수 있었다.

또한, 3번째 입출력 예의 orders 입력을 보면 “WXA”와 같이 정렬되지 않은 상태로 들어오는 데이터가 있는데, 이는 조합을 구할때 문제가 되므로 정렬해주자

코드

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map.Entry;

class Solution {

    static HashMap<String, Integer> map = new HashMap<>();
    public String[] solution(String[] orders, int[] course) {
        ArrayList<String> arr = new ArrayList<>();
        String[] answer = {};
        for(int i =0;i<orders.length;i++){
            // 2. 각 문자열을 문자형 배열로 변환.
            char[] charArr = orders[i].toCharArray();
            // 3. 해당 문자형 배열을 정렬.
            Arrays.sort(charArr);
            // 4. 정렬된 문자형 배열을 문자열로 변환해 저장.
            orders[i] = String.valueOf(charArr);
        }
        for (int i = 0; i < course.length; i++) {
            map.clear();
            int max = Integer.MIN_VALUE;
            for (int j = 0; j < orders.length; j++) {
                if (course[i] <= orders[j].length()) {
                    String s = orders[j];
                    String res = "";
                    combi(s, res, 0, 0, course[i]);
                }
            }
            for(Entry<String,Integer> entry : map.entrySet()){
                max = Math.max(max,entry.getValue());
            }
            for(Entry<String,Integer> entry : map.entrySet()){
                if(max >=2 && entry.getValue() == max)
                    arr.add(entry.getKey());
            }
        }
        Collections.sort(arr);
        
        return arr.toArray(answer);
    }

    public static void combi(String origin, String result, int idx, int cnt, int n) {
        if (cnt == n) {
            map.put(result, map.getOrDefault(result, 0) + 1);
            return;
        }
        for (int i = idx; i < origin.length(); i++) {
                result += origin.charAt(i);
                combi(origin, result, i+1, cnt+1, n);
                result = result.substring(0, result.length()-1);
            }
    }
}

문제

https://school.programmers.co.kr/learn/courses/30/lessons/250134?language=java

풀이

처음 실패한 풀이

빨간 수레와 파란 수레를 각각 다르게 DFS를 수행하고 이동 여부를 체크하여 따로 수레를 이동시키게끔 구현 했으나, 턴 카운팅 하는 과정에서 어려움을 느껴 포기했다.

이후 다른 풀이를 보고, 빨간 수레와 파란 수레 각각 4개의 경우로 이동 시키므로 4X4개의 경우로 한번에 이동시키게끔 하는 풀이로 수정 했다.

또한, 각각의 수레마다 이동한 위치로 다시 돌아갈 수 없으며, 서로 이동한 위치로는 이동이 가능하다. 따라서 이를 위해 각각 visited 배열을 활용했다.

코드

class Solution {
     static int EMPTY_SPACE = 0;
    static int RED_START = 1;
    static int BLUE_START = 2;
    static int RED_END = 3;
    static int BLUE_END = 4;
    static int WALL = 5;
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};
    static int[] originRedPos = new int[2];
    static int[] originBluePos = new int[2];
    static int[] redEndPos = new int[2];
    static int[] blueEndPos = new int[2];
    static int count = Integer.MAX_VALUE;
    static boolean[][] redVisited;
    static boolean[][] blueVisited;
    static int[][] arr;
    public int solution(int[][] maze) {
        int answer = 0;

        arr = maze;
        // red 먼저 시작, blue 먼저 시작
        for (int i = 0; i < maze.length; i++) {
            for (int j = 0; j < maze[i].length; j++) {
                if (maze[i][j] == RED_START) {
                    originRedPos[0] = i;
                    originRedPos[1] = j;
                } else if (maze[i][j] == BLUE_START) {
                    originBluePos[0] = i;
                    originBluePos[1] = j;
                } else if (maze[i][j] == RED_END) {
                    redEndPos[0] = i;
                    redEndPos[1] = j;
                } else if (maze[i][j] == BLUE_END) {
                    blueEndPos[0] = i;
                    blueEndPos[1] = j;
                }
            }
        }
        redVisited = new boolean[maze.length][maze[0].length];
        blueVisited = new boolean[maze.length][maze[0].length];
        redVisited[originRedPos[0]][originRedPos[1]] = true;
        blueVisited[originBluePos[0]][originBluePos[1]] = true;
        dfs(originRedPos[1], originRedPos[0], originBluePos[1], originBluePos[0], 0);
        if (count == Integer.MAX_VALUE) count = 0;
        return count;
    }

 
        public static void dfs(int redX, int redY, int blueX, int blueY, int round) {
        if (redY == redEndPos[0] && redX == redEndPos[1] && blueY == blueEndPos[0] && blueX == blueEndPos[1]) {
            count = Math.min(round, count);
            return;
        }
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                int[] newRedPos = new int[2];
                int[] newBluePos = new int[2];
                boolean skipRedCheck = false;
                boolean skipBlueCheck = false;
                // 새로운 이동 지점 선택
                if (redY == redEndPos[0] && redX == redEndPos[1]) {
                    newRedPos[0] = redY;
                    newRedPos[1] = redX;
                    skipRedCheck = true;
                } else {
                    newRedPos[0] = redY + dy[i];
                    newRedPos[1] = redX + dx[i];
                }
                if (blueY == blueEndPos[0] && blueX == blueEndPos[1]) {
                    newBluePos[0] = blueY;
                    newBluePos[1] = blueX;
                    skipBlueCheck = true;
                } else {
                    newBluePos[0] = blueY + dy[j];
                    newBluePos[1] = blueX + dx[j];
                }

                 // 이동 가능 여부 체크
                if (newRedPos[0] < 0 || newRedPos[1] < 0 || newBluePos[0] < 0 || newBluePos[1] < 0
                || newRedPos[0] >= arr.length || newRedPos[1] >= arr[0].length || newBluePos[0] >= arr.length || newBluePos[1] >= arr[0].length) continue;

                if (arr[newRedPos[0]][newRedPos[1]] == WALL || arr[newBluePos[0]][newBluePos[1]] == WALL) continue;

                // 이미 도착한 경우 움직이지 않게되므로 중복체크 하지 않음
                if (!skipRedCheck) {
                    if (redVisited[newRedPos[0]][newRedPos[1]]) continue;
                }
                if (!skipBlueCheck) {
                    if (blueVisited[newBluePos[0]][newBluePos[1]]) continue;
                }

                // 수레가 같은 곳으로 이동하려는 경우
                if (newRedPos[0] == newBluePos[0] && newRedPos[1] == newBluePos[1]) continue;
                if (newRedPos[0] == blueY && newRedPos[1] == blueX && newBluePos[0] == redY && newBluePos[1] == redX) continue;
                // 이동 및 round 추가
                redVisited[newRedPos[0]][newRedPos[1]] = true;
                blueVisited[newBluePos[0]][newBluePos[1]] = true;
                dfs(newRedPos[1], newRedPos[0], newBluePos[1], newBluePos[0], round + 1);
                redVisited[newRedPos[0]][newRedPos[1]] = false;
                blueVisited[newBluePos[0]][newBluePos[1]] = false;
            }
        }
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 128 MB 2792 1210 897 47.335%

문제

지민이는 카지노의 딜러이고, 지금 3명의 플레이어(0, 1, 2)가 있다. 이 게임은 N개의 카드를 이용한다. (0 ~ N-1번)

일단 지민이는 카드를 몇 번 섞은 다음에, 그것을 플레이어들에게 나누어 준다. 0번째 위치에 있던 카드가 플레이어 0에게 가고, 1번째 위치에 있던 카드는 플레이어 1에게 가고, 2번째 위치에 있던 카드는 플레이어 2에게 가고, 3번째 위치에 있던 카드는 플레이어 0에게 가고, 이런식으로 카드를 나누어 준다. 하지만, 지민이는 약간 사기를 치려고 한다.

지민이는 처음에 카드를 섞기 전에 카드의 순서를 알고 있고, 이 정보를 이용해 각 카드가 특정한 플레이어에게 보내지게 할 것이다.

카드를 한 번 섞을 때는 주어진 방법을 이용해서만 섞을 수 있고, 이 방법은 길이가 N인 수열 S로 주어진다. 카드를 한 번 섞고 나면 i번째 위치에 있던 카드는 S[i]번째 위치로 이동하게 된다.

각 카드가 어떤 플레이어에게 가야 하는지에 대한 정보는 길이가 N인 수열 P로 주어진다. 맨 처음 i번째 위치에 있던 카드를 최종적으로 플레이어 P[i] 에게 보내야한다.

지민이가 목적을 달성하기 위해 필요한 카드 섞는 횟수의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. N은 3보다 크거나 같고, 48보다 작거나 같은 3의 배수이다.

둘째 줄에 길이가 N인 수열 P가 주어진다. 수열 P에 있는 수는 0, 1, 2 중 하나이다.

셋째 줄에 길이가 N인 수열 S가 주어진다. 수열 S에 있는 수는 모두 N-1보다 작거나 같은 자연수 또는 0이고 중복되지 않는다.

출력

첫째 줄에 몇 번 섞어야 하는지 출력한다. 만약, 섞어도 섞어도 카드를 해당하는 플레이어에게 줄 수 없다면, -1을 출력한다.

풀이

가장 중요한 것은 섞어도 카드를 해당하는 플레이어에게 줄 수 없는 경우를 구하는 것인데, 나 같은 경우 섞다가 맨 처음 형태와 같은 형태가 나온다면 (싸이클이 있다면) 원하는 플레이어 에게 줄 수 없다고 생각했다.

따라서 아래와 같이 반복 계산 해줬다.

  1. 한번이라도 섞고 난 이후에 처음 형태와 같아졌을 경우 반복을 종료하고 -1 리턴
  2. 0,1,2 순서대로 잘 섞여졌다면 반복을 종료
  3. 카드 섞기
  4. 섞은 수 올려주기

문제를 제대로 못 읽어서 N, S를 거꾸로 생각하는 바람에 많은 시간을 사용했다.

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class B1091 {
    static int N;
    static int[] P;
    static int[] S;
    static int[] origin;
    static int[] tmp;
    static int cnt;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());

        P = new int[N];
        origin = new int[N];
        tmp = new int[N];
        S = new int[N];

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            P[i] = Integer.parseInt(st.nextToken());
            origin[i] = P[i];
        }

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            S[i] = Integer.parseInt(st.nextToken());
        }
        
        while (true) {
            if (cnt > 0 && isEqual()) {
                cnt = -1;
                break;
            }
            if (isStop()) {
                break;
            }
            swap();
            cnt++;
        }
        System.out.println(cnt);
    }
    public static boolean isEqual() {
        for (int i = 0; i < N; i++) {
            if (tmp[i] != origin[i]) return false;
        }
        return true;
    }
    public static boolean isStop() {
        for (int i = 0; i < N; i++) {
            if (P[i] != i % 3) return false;
        }
        return true;
    }
    public static void swap() {
        for (int i = 0; i < N; i++) {
            tmp[S[i]] = P[i];
        }
        P = Arrays.copyOf(tmp, tmp.length);
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 128 MB 1515 457 365 33.610%

문제

민식이는 다음과 같이 두 개의 명령만 지원하는 새로운 텍스트 에디터를 만들었다.

  • “type c" : 현재 글의 가장 뒤에 문자 c를 추가한다.
  • “undo t" : 이전 t초동안 수행된 작업을 역순으로 되돌린다.

처음 텍스트 에디터는 비어있다.

예를 들어, 다음과 같은 명령을 진행했다고 하자.

  • 1초 : type a
  • 2초 : type b
  • 3초 : type c
  • 5초 : undo 3

3초가 끝날 때, 텍스트는 "abc"이다. 5초때, 이전 3초동안 한 작업을 역순으로 되돌려야 한다. c는 지워지고, b도 지워질 것이다. 따라서 a만 남는다.

되돌리기가 되돌리기 될 수도 있다.

예를 들어

  • 1초 : type a
  • 2초 : type b
  • 3초 : undo 2
  • 4초 : undo 2

2초일 때, 텍스트는 “ab"이다. 3초때 모든 것이 되돌리기 되므로 텍스트는 빈 텍스트가 되고, 4초때 3초때 되돌리기 한 것이 되돌리기 되고, 2초때 type b한 것이 지워진다. 따라서 텍스트는 ”a"가 된다.

명령과 수행한 시간이 주어질 때, 마지막에 남은 텍스트를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 명령의 개수 N이 주어진다. 둘째 줄부터 N개의 줄에 명령과 수행한 시간이 주어진다. 항상 시간이 오름차순으로 주어지고, type c에서 c는 알파벳 소문자, undo t에서 t는 1보다 크거나 같고 109보다 작거나 같은 자연수이다. N은 50보다 작거나 같은 자연수이다.

출력

첫째 줄에 모든 명령을 수행한 후에 남아있는 텍스트를 출력한다.

풀이

아이디어를 떠올리는것이 굉장히 어려워 해답을 봤음.

처음엔 스택을 통해 undo 시 해당 로그를 저장하고 복구하는 형식으로 구현하려고 했는데, undo가 중첩되었을 때 구현을 어떻게 해야할지 잘 떠오르지 않았다.

그러나 해답을 보던 중 아래의 방법을 사용하여 다시 풀어봤다.

각 명령이 들어올때마다 벡터에 해당 시간과 문자를 저장하고 undo 명령시 (현재 시간 - t)초 이전의 상태를 불러오면 됩니다.

type 명령의 경우, 리스트의 가장 끝에 있는 데이터에 추가로 문자를 더해준 후 리스트에 추가한다.

undo 명령의 경우 리스트의 끝부터 순회하며, 리스트의 아이템의 시간보다 입력된 시간 - undo 시간이 크다면 해당 문자열을 리스트에 추가한다.(undo가 적용된 문자열을 리턴)

되돌리기라는 이름을 듣고 바로 스택을 써야한다고만 생각했는데, 문제를 보는 시각을 더 늘리자..

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Stack;
import java.util.StringTokenizer;

public class B1360 {

    static ArrayList<Pair> list = new ArrayList<>();

    public static class Pair {
        int first;
        String second;
        public Pair(int first, String second) {
            this.first = first;
            this.second = second;
        }
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());

        list.add(new Pair(0, ""));
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());

            String prompt = st.nextToken();

            if (prompt.equals("type")) {
                String character = st.nextToken();
                int time = Integer.parseInt(st.nextToken());
                list.add(new Pair(time, list.get(list.size()-1).second + character));
            } else {
                int undo = Integer.parseInt(st.nextToken());
                int time = Integer.parseInt(st.nextToken());
                list.add(new Pair(time, getStr(time-undo)));
            }
        }
        System.out.println(list.get(list.size()-1).second);

    }
    public static String getStr(int second) {
        for (int i = list.size()-1; i >= 0; i--) {
            if (list.get(i).first < second) {
                return list.get(i).second;
            }
        }
        return "";
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 128 MB 7011 3924 3058 55.109%

문제

한수는 캠프를 마치고 집에 돌아가려 한다. 한수는 현재 왼쪽 아래점에 있고 집은 오른쪽 위에 있다. 그리고 한수는 집에 돌아가는 방법이 다양하다. 단, 한수는 똑똑하여 한번 지나친 곳을 다시 방문하지는 않는다.

      cdef  ...f  ..ef  ..gh  cdeh  cdej  ...f
      bT..  .T.e  .Td.  .Tfe  bTfg  bTfi  .Tde
      a...  abcd  abc.  abcd  a...  a.gh  abc.
거리 :  6     6     6     8     8    10    6

위 예제는 한수가 집에 돌아갈 수 있는 모든 경우를 나타낸 것이다. T로 표시된 부분은 가지 못하는 부분이다. 문제는 R x C 맵에 못가는 부분이 주어지고 거리 K가 주어지면 한수가 집까지도 도착하는 경우 중 거리가 K인 가짓수를 구하는 것이다.

입력

첫 줄에 정수 R(1 ≤ R ≤ 5), C(1 ≤ C ≤ 5), K(1 ≤ K ≤ R×C)가 공백으로 구분되어 주어진다. 두 번째부터 R+1번째 줄까지는 R×C 맵의 정보를 나타내는 '.'과 'T'로 구성된 길이가 C인 문자열이 주어진다.

출력

첫 줄에 거리가 K인 가짓수를 출력한다.

풀이

R,C의 범위가 매우 작으므로 DFS를 활용하여 문제를 풀었다.

왼쪽 아래 (r-1, 0) 부터 오른쪽 위 (0, c-1) 까지 이동하는 경우 중 k번의 이동으로 도달 가능한 갯수를 작성하는 문제이므로 (r-1, 0) 부터 (0, c-1) 까지 depth를 늘려가며 1칸씩 이동하도록 해줬다. 별다른 어려움은 없이 풀었다.

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.Buffer;
import java.util.StringTokenizer;

public class B1189 {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};
    static int r;
    static int c;
    static int k;
    static int[][] arr;
    static boolean[][] visited;

    static int count;

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

        r = Integer.parseInt(st.nextToken());
        c = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());

        arr = new int[r][c];
        visited = new boolean[r][c];

        for (int i = 0; i < r; i++) {
            st = new StringTokenizer(br.readLine());
            String s = st.nextToken();
            for (int j = 0; j < s.length(); j++) {
                if (s.charAt(j) == '.') {
                    arr[i][j] = 0;
                } else {
                    arr[i][j] = 1;
                }
            }
        }
        visited[r-1][0] = true;
        dfs(r-1, 0, 1);
        System.out.println(count);
    }
    public static void dfs(int i, int j, int depth) {
        if (i == 0 && j == c-1) {
            if (depth == k) count++;
            return;
        }
        for (int l = 0; l < dy.length; l++) {
            int ny = i + dy[l];
            int nx = j + dx[l];

            if (ny < 0 || nx < 0 || ny >= r || nx >= c) continue;
            if (visited[ny][nx]) continue;
            if (arr[ny][nx] == 1) continue;
            visited[ny][nx] = true;
            dfs(ny, nx, depth + 1);
            visited[ny][nx] = false;
        }
    }
}

문제

https://school.programmers.co.kr/learn/courses/30/lessons/42627

풀이

우선순위 큐를 활용하여 문제를 풀었음. (정렬이 필요하므로)

 

정렬

  1. 작업큐
    1. 작업큐의 경우 기본적으로 작업이 들어온 순서대로 정렬되어야 한다. 또한, 같은 시간에 작업이 들어온 경우, 작업 시간이 더 짧은 작업을 먼저 처리하는것이 효율적이다.
  2. 대기큐
    1. 작업 시간이 짧은 순서대로 정렬해주자.

작업 고르기

  1. 현재 대기중인 작업이 있는 경우
    1. 대기 큐에서 작업시간이 가장 짧은 작업을 고른다.
    2. 만약 현재 시간보다 작업 큐에서 꺼낸 작업의 시작시간이 더 크다면, 시간을 꺼낸 작업의 시작시간으로 변경해준다. (새로운 작업은 현재 시간보다 100초 이후에 시작될수도 있다.)
  2. 현재 대기중인 작업이 없는 경우
    1. 요청이 들어온 순서대로 작업을 고른다.

작업 실행

작업 시간만큼 반복하면서 시간을 더해주고, 현재 시간에 새로운 작업이 추가되면 작업큐에서 대기큐로 옮겨준다.

작업 완료

작업이 완료되었다면 걸린 시간을 정답에 추가해준다. 작업큐와 대기큐가 모두 비었다면, 전체 작업의 갯수만큼 나눠서 리턴해준다.

코드

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.PriorityQueue;

public class Solution {
    static int[] current;
    static int time;
        public int solution(int[][] jobs) {
        int answer = 0;
        int count = jobs.length;
        PriorityQueue<int[]> list = new PriorityQueue<int[]>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[0] == o2[0]) {
                    return o1[1]-o2[1];
                }
                else return o1[0]-o2[0];
            }
        });
               PriorityQueue<int[]> wait = new PriorityQueue<int[]>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        });

        for (int i = 0; i < jobs.length; i++) {
            list.add(jobs[i]);
        }

        while (true) {
            if (wait.isEmpty() && list.isEmpty()) break;
            // 맨 위에 있는거 꺼내기
            if (wait.isEmpty()) {
                current = list.poll();
                if (current[0] > time) {
                    time = current[0];
                }
            } else {
                current = wait.poll();
            }

            for (int i = 0; i < current[1]; i++) {
                if (!list.isEmpty() && list.peek()[0] <= time) {
                    wait.add(list.poll());
                }
                time++;
            }
            answer += time - current[0];
        }

 
        return answer / count;
    }

}

 

 

 

한국어 읽는게 제일 어렵다..

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 128 MB 29631 16632 11947 55.129%

문제

아래 <그림 1>과 같이 정사각형 칸들로 이루어진 사각형 모양의 판이 있고, 그 위에 얇은 치즈(회색으로 표시된 부분)가 놓여 있다. 판의 가장자리(<그림 1>에서 네모 칸에 X친 부분)에는 치즈가 놓여 있지 않으며 치즈에는 하나 이상의 구멍이 있을 수 있다.

이 치즈를 공기 중에 놓으면 녹게 되는데 공기와 접촉된 칸은 한 시간이 지나면 녹아 없어진다. 치즈의 구멍 속에는 공기가 없지만 구멍을 둘러싼 치즈가 녹아서 구멍이 열리면 구멍 속으로 공기가 들어가게 된다. <그림 1>의 경우, 치즈의 구멍을 둘러싼 치즈는 녹지 않고 ‘c’로 표시된 부분만 한 시간 후에 녹아 없어져서 <그림 2>와 같이 된다.

https://upload.acmicpc.net/9b0f0cfb-007d-4ea8-8e6f-e397728b5c8e/-/preview/

<그림 1> 원래 치즈 모양

다시 한 시간 후에는 <그림 2>에서 ‘c’로 표시된 부분이 녹아 없어져서 <그림 3>과 같이 된다.

https://upload.acmicpc.net/b099f661-9788-4183-bd62-1e98e6f184e7/-/preview/

<그림 2> 한 시간 후의 치즈 모양

https://upload.acmicpc.net/58fc0743-794b-4e27-84e8-fe491ec7bf3d/-/preview/

<그림 3> 두 시간 후의 치즈 모양

<그림 3>은 원래 치즈의 두 시간 후 모양을 나타내고 있으며, 남은 조각들은 한 시간이 더 지나면 모두 녹아 없어진다. 그러므로 처음 치즈가 모두 녹아 없어지는 데는 세 시간이 걸린다. <그림 3>과 같이 치즈가 녹는 과정에서 여러 조각으로 나누어 질 수도 있다.

입력으로 사각형 모양의 판의 크기와 한 조각의 치즈가 판 위에 주어졌을 때, 공기 중에서 치즈가 모두 녹아 없어지는 데 걸리는 시간과 모두 녹기 한 시간 전에 남아있는 치즈조각이 놓여 있는 칸의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 사각형 모양 판의 세로와 가로의 길이가 양의 정수로 주어진다. 세로와 가로의 길이는 최대 100이다. 판의 각 가로줄의 모양이 윗 줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다. 치즈가 없는 칸은 0, 치즈가 있는 칸은 1로 주어지며 각 숫자 사이에는 빈칸이 하나씩 있다.

출력

첫째 줄에는 치즈가 모두 녹아서 없어지는 데 걸리는 시간을 출력하고, 둘째 줄에는 모두 녹기 한 시간 전에 남아있는 치즈조각이 놓여 있는 칸의 개수를 출력한다.

풀이

2638번 치즈와 같은 문제인데, 조건이 조금 달라졌다.

  1. 치즈는 모눈종이의 맨 가장자리에는 놓이지 않는다. 따라서, (0,0)은 항상 외부 공기라고 알 수 있다. 이를 통해 BFS로 외부 공기만 체크 해준다. 이렇게 하면 치즈 내부에 있는 공간은 순회하지 않고, 치즈 바깥에 있는 공기만을 방문할 수 있다.
  2. 이제 외부 공기, 치즈, 치즈 내부 공기를 구분할 수 있게 된다. 여기서 배열을 순회하면서 치즈면서 상,하,좌,우로 외부 공기가 한칸 이상 닿는 치즈는 곧 녹게되므로 체크해준다.
  3. 녹을 치즈가 선택되었다면 해당 치즈를 녹여 외부 공기로 만들어주고 카운트를 올린다.
  4. 이 과정을 치즈의 갯수가 0이 될때까지 반복하고, 녹이기 전 현재 치즈 갯수를 저장하고 출력한다.

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class B2636 {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};
    static int INSIDE_CHEESE = 0;
    static int CHEESE = 1;
    static int OUTSIDE_CHEESE = 2;
    static int WILL_MELTED = 3;
    static int N;
    static int M;
    static int[][] arr;
    static boolean[][] visited;

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

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        arr = new int[N][M];
        visited = new boolean[N][M];
        int cheeseCount = 0;
        int lastCheeseCount = 0;
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
                if (arr[i][j] == 1) cheeseCount++;
            }
        }
        while (true) {
            bfs(0,0);
            visited = new boolean[N][M];
            if (cheeseCount == 0) break;
            lastCheeseCount = cheeseCount;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (arr[i][j] == CHEESE) {
                        int cnt = 0;
                        for (int k = 0; k < dx.length; k++) {
                            int ny = dy[k] + i;
                            int nx = dx[k] + j;
                            if (arr[ny][nx] == OUTSIDE_CHEESE)
                                cnt++;
                        }
                        if (cnt >= 1)
                            arr[i][j] = WILL_MELTED;
                    }
                }
            }
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (arr[i][j] == WILL_MELTED) {
                        arr[i][j] = OUTSIDE_CHEESE;
                        cheeseCount--;
                    }
                }
            }

            count++;
        }
        System.out.println(count);
        System.out.println(lastCheeseCount);
    }
    public static void bfs(int i, int j) {
        Queue<int[]> queue = new LinkedList();
        queue.add(new int[]{i,j});
        while (!queue.isEmpty()) {
            int[] val = queue.poll();
            for (int k = 0; k < dx.length; k++) {
                int ny = dy[k] + val[0];
                int nx = dx[k] + val[1];

                if (ny < 0 || nx < 0 || ny >= N || nx >= M) continue;
                if (visited[ny][nx]) continue;
                if (arr[ny][nx] == CHEESE) continue;
                visited[ny][nx] = true;
                arr[ny][nx] = OUTSIDE_CHEESE;
                queue.add(new int[]{ny,nx});
            }
        }
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 128 MB 26868 12675 9471 46.220%

문제

N×M의 모눈종이 위에 아주 얇은 치즈가 <그림 1>과 같이 표시되어 있다. 단, N 은 세로 격자의 수이고, M 은 가로 격자의 수이다. 이 치즈는 냉동 보관을 해야만 하는데 실내온도에 내어놓으면 공기와 접촉하여 천천히 녹는다. 그런데 이러한 모눈종이 모양의 치즈에서 각 치즈 격자(작 은 정사각형 모양)의 4변 중에서 적어도 2변 이상이 실내온도의 공기와 접촉한 것은 정확히 한시간만에 녹아 없어져 버린다. 따라서 아래 <그림 1> 모양과 같은 치즈(회색으로 표시된 부분)라면 C로 표시된 모든 치즈 격자는 한 시간 후에 사라진다.

<그림 1>

<그림 2>와 같이 치즈 내부에 있는 공간은 치즈 외부 공기와 접촉하지 않는 것으로 가정한다. 그러므 로 이 공간에 접촉한 치즈 격자는 녹지 않고 C로 표시된 치즈 격자만 사라진다. 그러나 한 시간 후, 이 공간으로 외부공기가 유입되면 <그림 3>에서와 같이 C로 표시된 치즈 격자들이 사라지게 된다.

<그림 2>

<그림 3>

모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정한다. 입력으로 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 모눈종이의 크기를 나타내는 두 개의 정수 N, M (5 ≤ N, M ≤ 100)이 주어진다. 그 다음 N개의 줄에는 모눈종이 위의 격자에 치즈가 있는 부분은 1로 표시되고, 치즈가 없는 부분은 0으로 표시된다. 또한, 각 0과 1은 하나의 공백으로 분리되어 있다.

출력

출력으로는 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 정수로 첫 줄에 출력한다.

풀이

  1. 치즈는 모눈종이의 맨 가장자리에는 놓이지 않는다. 따라서, (0,0)은 항상 외부 공기라고 알 수 있다. 이를 통해 BFS로 외부 공기만 체크 해준다. 이렇게 하면 치즈 내부에 있는 공간은 순회하지 않고, 치즈 바깥에 있는 공기만을 방문할 수 있다.
  2. 이제 외부 공기, 치즈, 치즈 내부 공기를 구분할 수 있게 된다. 여기서 배열을 순회하면서 치즈면서 상,하,좌,우로 외부 공기가 두칸 이상 닿는 치즈는 곧 녹게되므로 체크해준다.
  3. 녹을 치즈가 선택되었다면 해당 치즈를 녹여 외부 공기로 만들어주고 카운트를 올린다.
  4. 이 과정을 치즈의 갯수가 0이 될때까지 반복한다.

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class B2638 {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};
    static int INSIDE_CHEESE = 0;
    static int CHEESE = 1;
    static int OUTSIDE_CHEESE = 2;
    static int WILL_MELTED = 3;
    static int N;
    static int M;
    static int[][] arr;
    static boolean[][] visited;

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

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        arr = new int[N][M];
        visited = new boolean[N][M];
        int cheeseCount = 0;
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
                if (arr[i][j] == 1) cheeseCount++;
            }
        }
        while (true) {
            bfs(0,0);
            visited = new boolean[N][M];
            if (cheeseCount == 0) break;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (arr[i][j] == CHEESE) {
                        int cnt = 0;
                        for (int k = 0; k < dx.length; k++) {
                            int ny = dy[k] + i;
                            int nx = dx[k] + j;
                            if (arr[ny][nx] == OUTSIDE_CHEESE)
                                cnt++;
                        }
                        if (cnt >= 2)
                            arr[i][j] = WILL_MELTED;
                    }
                }
            }
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (arr[i][j] == WILL_MELTED) {
                        arr[i][j] = OUTSIDE_CHEESE;
                        cheeseCount--;
                    }
                }
            }
            count++;
        }

        System.out.println(count);
    }
    public static void bfs(int i, int j) {
        Queue<int[]> queue = new LinkedList();
        queue.add(new int[]{i,j});
        while (!queue.isEmpty()) {
            int[] val = queue.poll();
            for (int k = 0; k < dx.length; k++) {
                int ny = dy[k] + val[0];
                int nx = dx[k] + val[1];

                if (ny < 0 || nx < 0 || ny >= N || nx >= M) continue;
                if (visited[ny][nx]) continue;
                if (arr[ny][nx] == CHEESE) continue;
                visited[ny][nx] = true;
                arr[ny][nx] = OUTSIDE_CHEESE;
                queue.add(new int[]{ny,nx});
            }
        }
    }
}

+ Recent posts