문제

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);
            }
    }
}

문제

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

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});
            }
        }
    }
}

문제

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

1 초 (추가 시간 없음) 512 MB 39509 18717 12827 46.147%

문제

유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의 번호이고, 행과 열의 번호는 1부터 시작한다. 각각의 칸은 빈 칸이거나 벽이다.

오늘은 집 수리를 위해서 파이프 하나를 옮기려고 한다. 파이프는 아래와 같은 형태이고, 2개의 연속된 칸을 차지하는 크기이다.

파이프는 회전시킬 수 있으며, 아래와 같이 3가지 방향이 가능하다.

 

파이프는 매우 무겁기 때문에, 유현이는 파이프를 밀어서 이동시키려고 한다. 벽에는 새로운 벽지를 발랐기 때문에, 파이프가 벽을 긁으면 안 된다. 즉, 파이프는 항상 빈 칸만 차지해야 한다.

파이프를 밀 수 있는 방향은 총 3가지가 있으며, →, ↘, ↓ 방향이다. 파이프는 밀면서 회전시킬 수 있다. 회전은 45도만 회전시킬 수 있으며, 미는 방향은 오른쪽, 아래, 또는 오른쪽 아래 대각선 방향이어야 한다.

파이프가 가로로 놓여진 경우에 가능한 이동 방법은 총 2가지, 세로로 놓여진 경우에는 2가지, 대각선 방향으로 놓여진 경우에는 3가지가 있다.

아래 그림은 파이프가 놓여진 방향에 따라서 이동할 수 있는 방법을 모두 나타낸 것이고, 꼭 빈 칸이어야 하는 곳은 색으로 표시되어져 있다.

 

대각선

가장 처음에 파이프는 (1, 1)와 (1, 2)를 차지하고 있고, 방향은 가로이다. 파이프의 한쪽 끝을 (N, N)로 이동시키는 방법의 개수를 구해보자.

입력

첫째 줄에 집의 크기 N(3 ≤ N ≤ 16)이 주어진다. 둘째 줄부터 N개의 줄에는 집의 상태가 주어진다. 빈 칸은 0, 벽은 1로 주어진다. (1, 1)과 (1, 2)는 항상 빈 칸이다.

출력

첫째 줄에 파이프의 한쪽 끝을 (N, N)으로 이동시키는 방법의 수를 출력한다. 이동시킬 수 없는 경우에는 0을 출력한다. 방법의 수는 항상 1,000,000보다 작거나 같다.

풀이

DFS를 활용하여 N,N으로 도달하는 경우의 수를 구했다.

문제 설명에서 보이듯이 오른쪽, 중간, 아래로 갈 수 있는 경우가 있고, 이전에 어떤 경로로 왔는지에 따라 갈 수 있는 방향이 달라진다. 따라서 아래와 같이 정리 후 dfs를 수행하도록 했다.

  1. 오른쪽으로 이동 했을 경우
    1. 오른쪽 이동 (오른쪽에 벽이 있을 시 이동 불가)
    2. 중간 이동 (오른쪽, 중간, 아래에 벽이 있을 시 이동 불가)
  2. 중간으로 이동 했을 경우
    1. 오른쪽 이동 (오른쪽에 벽이 있을 시 이동 불가)
    2. 중간 이동 (오른쪽, 중간, 아래에 벽이 있을 시 이동 불가)
    3. 아래쪽 이동 (아래에 벽이 있을 시 이동 불가)
  3. 아래쪽으로 이동 했을 경우
    1. 중간 이동 (오른쪽, 중간, 아래에 벽이 있을 시 이동 불가)
    2. 아래쪽 이동 (아래에 벽이 있을 시 이동 불가)

또한, N,N에 도달했을 경우, 더이상 오른쪽으로 이동 할 수 없는 경우, 아래쪽으로 이동할 수 없는 경우에 대해 처리해줬다.

코드

package com.company.baekjoon;

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

public class B17070 {
    static int N;
    static int[][] arr;
    static int count = 0;

    static int TO_RIGHT = 1;
    static int TO_MIDDLE = 2;
    static int TO_BOTTOM = 3;
    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());
        arr = new int[N][N];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        int currentY = 0;
        int currentX = 1;
        dfs(currentY, currentX, TO_RIGHT);

        System.out.println(count);
    }
    public static void dfs(int cy, int cx, int type) {
        if (cy == N - 1 && cx == N - 1) {
            count++;
            return;
        } else if (type == TO_RIGHT && cx == N - 1)
            return;
        else if (type == TO_BOTTOM && cy == N - 1)
            return;

        if (type == TO_RIGHT) {
            if (cx + 1 < N && arr[cy][cx+1] != 1) {
                dfs(cy, cx+1, TO_RIGHT);
            }
            if (cx + 1 < N && cy + 1 < N && arr[cy+1][cx+1] != 1 && arr[cy+1][cx] != 1 && arr[cy][cx+1] != 1) {
                dfs(cy+1,cx+1, TO_MIDDLE);
            }
        } else if (type == TO_MIDDLE) {
            if (cx + 1 < N && arr[cy][cx+1] != 1) {
                dfs(cy, cx+1, TO_RIGHT);
            }
            if (cx + 1 < N && cy + 1 < N && arr[cy+1][cx+1] != 1 && arr[cy+1][cx] != 1 && arr[cy][cx+1] != 1) {
                dfs(cy+1,cx+1, TO_MIDDLE);
            }
            if (cy + 1 < N && arr[cy+1][cx] != 1) {
                dfs(cy+1, cx, TO_BOTTOM);
            }
        } else if (type == TO_BOTTOM) {
            if (cx + 1 < N && cy + 1 < N && arr[cy+1][cx+1] != 1 && arr[cy+1][cx] != 1 && arr[cy][cx+1] != 1) {
                dfs(cy+1,cx+1, TO_MIDDLE);
            }
            if (cy + 1 < N && arr[cy+1][cx] != 1) {
                dfs(cy+1, cx, TO_BOTTOM);
            }
        }
    }
}

문제

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

2 초 128 MB 42619 16415 12036 37.043%

문제

동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인지 알아보자. 물론 중간에 다른 도시를 경유해서 여행을 할 수도 있다. 예를 들어 도시가 5개 있고, A-B, B-C, A-D, B-D, E-A의 길이 있고, 동혁이의 여행 계획이 E C B C D 라면 E-A-B-C-B-C-B-D라는 여행경로를 통해 목적을 달성할 수 있다.

도시들의 개수와 도시들 간의 연결 여부가 주어져 있고, 동혁이의 여행 계획에 속한 도시들이 순서대로 주어졌을 때 가능한지 여부를 판별하는 프로그램을 작성하시오. 같은 도시를 여러 번 방문하는 것도 가능하다.

입력

첫 줄에 도시의 수 N이 주어진다. N은 200이하이다. 둘째 줄에 여행 계획에 속한 도시들의 수 M이 주어진다. M은 1000이하이다. 다음 N개의 줄에는 N개의 정수가 주어진다. i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보를 의미한다. 1이면 연결된 것이고 0이면 연결이 되지 않은 것이다. A와 B가 연결되었으면 B와 A도 연결되어 있다. 마지막 줄에는 여행 계획이 주어진다. 도시의 번호는 1부터 N까지 차례대로 매겨져 있다.

출력

첫 줄에 가능하면 YES 불가능하면 NO를 출력한다.

풀이

예제의 여행 계획을 보면 각 노드들이 모두 연결되어 있기만 하면 어떻게든 경유하여 목적을 달성할 수 있다. 따라서, dfs 또는 bfs를 이용하여 연결된 노드를 모두 탐색한다. 나의 경우 dfs를 통해 풀었고, 검색 기준은 여행계획의 첫번째 도시로 설정했다.

코드

package com.company.baekjoon;

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

public class B1976 {
	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());
		st = new StringTokenizer(br.readLine());

		int [][] graph = new int[N][N];
		boolean[] visited = new boolean[N];
		boolean[] target = new boolean[N];
		int M = Integer.parseInt(st.nextToken());

		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());

			for (int j = 0; j < N; j++) {
				graph[i][j] = Integer.parseInt(st.nextToken());
			}
		}

		st = new StringTokenizer(br.readLine());

		int start = 0;
		boolean checkStart = false;
		while (st.hasMoreTokens()) {
			int val = Integer.parseInt(st.nextToken()) - 1;
			if (!checkStart) {
				start = val;
				checkStart = true;
			}
			target[val] = true;
		}

		dfs(graph,visited, start);
		boolean isTravel = checkCity(visited, target);

		if (isTravel) {
			System.out.println("YES");
		} else {
			System.out.println("NO");
		}
	}
	public static boolean checkCity(boolean[] visited, boolean[] target) {
		boolean isTravel = false;
		for (int i = 0; i <visited.length; i++) {
			if (target[i]) { // 꼭 들러야 하는곳이라면
				if (visited[i]) {
					isTravel = true;
				} else {
					isTravel = false;
					break;
				}
			}
		}
		return isTravel;
	}
	public static void dfs(int[][] graph, boolean[] visited, int start) {
		visited[start] = true;
		for (int i = 0; i < graph.length; i++) {
			if (!visited[i] && graph[start][i] == 1) {
				dfs(graph, visited, i);
			}
		}
	}
}

문제

문제 설명

두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.

1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다. 2. words에 있는 단어로만 변환할 수 있습니다.

예를 들어 begin이 "hit", target가 "cog", words가 ["hot","dot","dog","lot","log","cog"]라면 "hit" -> "hot" -> "dot" -> "dog" -> "cog"와 같이 4단계를 거쳐 변환할 수 있습니다.

두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 각 단어는 알파벳 소문자로만 이루어져 있습니다.
  • 각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.
  • words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.
  • begin과 target은 같지 않습니다.
  • 변환할 수 없는 경우에는 0를 return 합니다.

입출력 예

begin target words return

"hit" "cog" ["hot", "dot", "dog", "lot", "log", "cog"] 4
"hit" "cog" ["hot", "dot", "dog", "lot", "log"] 0

입출력 예 설명

예제 #1

문제에 나온 예와 같습니다.

예제 #2

target인 "cog"는 words 안에 없기 때문에 변환할 수 없습니다.

풀이

BFS로 풀었다. 변환 횟수와 현재 문자열로 Pair를 만든 후에 큐에 추가한다, 해당 문자열을 변환 가능할 경우 변환 횟수를 1회 늘리고 변환 가능한 문자를 다시 큐에 추가한다. 이를 반복하여 큐에서 꺼낸 값이 target과 동일할 경우 리턴하도록 작성했다.

코드

import javafx.util.Pair;
import java.util.Comparator;
import java.util.PriorityQueue;

class Solution {
    public int solution(String begin, String target, String[] words) {
        int answer = 0;
        PriorityQueue<Pair<String,Integer>> queue = new PriorityQueue<>(new Comparator<Pair<String, Integer>>() {
            @Override
            public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
                return o1.getValue() - o2.getValue();
            }
        });
        boolean isContain = false;
        for (String word:
             words) {
            if (word.equals(target)) {
                isContain = true;
            }
        }
        if (!isContain)
            return 0;
        queue.add(new Pair<>(begin, 0));
        while (true) {
            Pair<String, Integer> p = queue.poll();
            if (p.getKey().equals(target)) {
                answer = p.getValue();
                break;
            }
            for (int i = 0; i < words.length; i++) {
                int diff = 0;
                for (int j = 0; j < p.getKey().length(); j++) {
                    if (words[i].charAt(j) != p.getKey().charAt(j)) {
                        diff++;
                    }
                }
                if (diff == 1) {
                    queue.add(new Pair<>(words[i], p.getValue()+1));
                }
            }
        }
        return answer;
    }
}

문제

문제 설명

네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있을 때 컴퓨터 A와 컴퓨터 C도 간접적으로 연결되어 정보를 교환할 수 있습니다. 따라서 컴퓨터 A, B, C는 모두 같은 네트워크 상에 있다고 할 수 있습니다.

컴퓨터의 개수 n, 연결에 대한 정보가 담긴 2차원 배열 computers가 매개변수로 주어질 때, 네트워크의 개수를 return 하도록 solution 함수를 작성하시오.

제한사항

  • 컴퓨터의 개수 n은 1 이상 200 이하인 자연수입니다.
  • 각 컴퓨터는 0부터 n-1인 정수로 표현합니다.
  • i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현합니다.
  • computer[i][i]는 항상 1입니다.

입출력 예

n computers return

3 [[1, 1, 0], [1, 1, 0], [0, 0, 1]] 2
3 [[1, 1, 0], [1, 1, 1], [0, 1, 1]] 1

입출력 예 설명

예제 #1

아래와 같이 2개의 네트워크가 있습니다.

예제 #2

아래와 같이 1개의 네트워크가 있습니다.

풀이

DFS 또는 BFS로 풀 수 있는 문제로, 나는 BFS를 사용해서 풀었다. 각 노드들을 순회하면서, 만약 방문하지 않은 노드라면 큐에 넣은 후 연결된 노드를 모두 탐색하여 방문처리 해준다. 방문처리가 끝났다면 answer 값을 증가 시켜주면 된다.

코드

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

class Solution {
    public int solution(int n, int[][] computers) {
        int answer = 0;
        boolean[] visited = new boolean[n];
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                queue.add(i);
                while (!queue.isEmpty()) {
                    int idx = queue.poll();
                    for (int j = 0; j < computers[idx].length; j++) {
                        if (!visited[j] && computers[idx][j] == 1) {
                            visited[j] = true;
                            queue.add(j);
                        }
                    }
                }
                answer++;
            }
        }

        return answer;
    }
}

문제

문제 설명

이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.

명령어 수신 탑(높이)

I 숫자 큐에 주어진 숫자를 삽입합니다.
D 1 큐에서 최댓값을 삭제합니다.
D -1 큐에서 최솟값을 삭제합니다.

이중 우선순위 큐가 할 연산 operations가 매개변수로 주어질 때, 모든 연산을 처리한 후 큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return 하도록 solution 함수를 구현해주세요.

제한사항

  • operations는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.
  • operations의 원소는 큐가 수행할 연산을 나타냅니다.
    • 원소는 “명령어 데이터” 형식으로 주어집니다.- 최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.
  • 빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.

입출력 예

operations return

["I 16", "I -5643", "D -1", "D 1", "D 1", "I 123", "D -1"] [0,0]
["I -45", "I 653", "D 1", "I -642", "I 45", "I 97", "D 1", "D -1", "I 333"] [333, -45]

입출력 예 설명

입출력 예 #1

  • 16과 -5643을 삽입합니다.
  • 최솟값을 삭제합니다. -5643이 삭제되고 16이 남아있습니다.
  • 최댓값을 삭제합니다. 16이 삭제되고 이중 우선순위 큐는 비어있습니다.
  • 우선순위 큐가 비어있으므로 최댓값 삭제 연산이 무시됩니다.
  • 123을 삽입합니다.
  • 최솟값을 삭제합니다. 123이 삭제되고 이중 우선순위 큐는 비어있습니다.

따라서 [0, 0]을 반환합니다.

입출력 예 #2

  • 45와 653을 삽입후 최댓값(653)을 삭제합니다. -45가 남아있습니다.
  • 642, 45, 97을 삽입 후 최댓값(97), 최솟값(-642)을 삭제합니다. -45와 45가 남아있습니다.
  • 333을 삽입합니다.

이중 우선순위 큐에 -45, 45, 333이 남아있으므로, [333, -45]를 반환합니다.

풀이

문제의 제목처럼, 우선순위 큐를 최댓값을 기준으로 꺼내는 큐, 최솟값을 기준으로 꺼내는 큐 두가지를 가지도록 한다. 이 후 I 연산이 나왔을 때 두 큐에 값을 추가해주고, D 연산이 나왔을 때에는 각 큐에서 값 제거 후 다른 큐에 남아있는 동일한 값을 제거해준다. 이후 answer 배열에 최댓값, 최솟값을 담아서 리턴해준다.

코드

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

class Solution {
    public int[] solution(String[] operations) {
        int[] answer = new int[2];
        PriorityQueue<Integer> maxQueue = new PriorityQueue<Integer>(Comparator.reverseOrder());
        PriorityQueue<Integer> minQueue = new PriorityQueue<Integer>();
        for (int i = 0; i < operations.length; i++) {
            String[] splited = operations[i].split(" ");
            if (splited[0].equals("I")) {
                maxQueue.add(Integer.parseInt(splited[1]));
                minQueue.add(Integer.parseInt(splited[1]));
            } else {
                if (splited[1].equals("1") && !maxQueue.isEmpty()) {
                    minQueue.remove(maxQueue.poll());
                } else if (splited[1].equals("-1") && !minQueue.isEmpty()) {
                    maxQueue.remove(minQueue.poll());
                }

            }
        }
        if (!maxQueue.isEmpty())
            answer[0] = maxQueue.poll();
        if (!minQueue.isEmpty())
            answer[1] = minQueue.poll();

        return answer;
    }
}

+ Recent posts