[BOJ] 15651. N과 M (3) - 중복순열 (Java)

Problem

제출일 : 2020-02-16

문제 풀이 시간 : 15M

난이도 : ★


link : https://www.acmicpc.net/problem/15651

자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.

  • 1부터 N까지 자연수 중에서 M개를 고른 수열
  • 같은 수를 여러 번 골라도 된다.

input

첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 7)

Output

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.

수열은 사전 순으로 증가하는 순서로 출력해야 한다.

Example

input

4 2

output

1 1
1 2
1 3
1 4
2 1
2 2
2 3
2 4
3 1
3 2
3 3
3 4
4 1
4 2
4 3
4 4

Solution & Inpression

N과M 세번째 문제

중복하여 선택이 가능하고 순서가 중요한것은 중복순열이다.

시간초과가 발생하여 StringBuilder을 이용하여 문제를 풀었습니다.

Code

언어 : JAVA

메모리 : 373,692 kb

실행시간 : 696 ms

import java.util.Scanner;

public class Main {
    static int N, M;
    static int[] rlt;
    static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        M = sc.nextInt();
        rlt = new int[M];
        solve(0, 0);
        System.out.println(sb);
    }

    private static void solve(int index, int depth) {
        if (depth == M) {
            for (int i = 0; i < M; i++) {
                sb.append(rlt[i] + " ");
            }
            sb.append("\n");
            return;
        }
        for (int i = 0; i < N; i++) {
            rlt[depth] = i + 1;
            solve(i, depth + 1);
        }

    }
} 

[BOJ] 11723. 집합 - 비트마스킹 (Java)

Problem

제출일 : 2020-02-16

문제 풀이 시간 : 15M

난이도 : ★


link : https://www.acmicpc.net/problem/11723

비어있는 공집합 S가 주어졌을 때, 아래 연산을 수행하는 프로그램을 작성하시오.

  • add x: S에 x를 추가한다. (1 ≤ x ≤ 20) S에 x가 이미 있는 경우에는 연산을 무시한다.
  • remove x: S에서 x를 제거한다. (1 ≤ x ≤ 20) S에 x가 없는 경우에는 연산을 무시한다.
  • check x: S에 x가 있으면 1을, 없으면 0을 출력한다.
  • toggle x: S에 x가 있으면 x를 제거하고, 없으면 x를 추가한다. (1 ≤ x ≤ 20)
  • all: S를 {1, 2, ..., 20} 으로 바꾼다.
  • empty: S를 공집합으로 바꾼다.

input

첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다.

둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.

Output

check 연산이 주어질때마다, 결과를 출력한다.

Example

input

26
add 1
add 2
check 1
check 2
check 3
remove 2
check 1
check 2
toggle 3
check 1
check 2
check 3
check 4
all
check 10
check 20
toggle 10
remove 20
check 10
check 20
empty
check 1
toggle 1
check 1
toggle 1
check 1

output

1
1
0
1
0
1
0
1
0
1
1
0
0
0
1
0

Solution & Inpression

비트마스킹문제지만 배열을 이용하여 문제를 풀었고 처음 제출했을때 시간 초과가 발생하여 StringBuilder 을 이용하여 출력시간을 줄여 문제를 해결하였습니다.

Code

언어 : JAVA

메모리 : 389,056 kb

실행시간 : 3,632 ms

import java.util.Arrays;
import java.util.Scanner;

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

        int M = sc.nextInt();
        boolean[] set = new boolean[21];
        for (int i = 0; i < M; i++) {
            switch (sc.next()) {
            case "add":
                set[sc.nextInt()] = true;
                break;
            case "remove":
                set[sc.nextInt()] = false;
                break;
            case "check":
                if (set[sc.nextInt()])
                    ans.append("1\n");
                else
                    ans.append("0\n");
                break;
            case "toggle":
                int tmp = sc.nextInt();
                set[tmp] = !set[tmp];
                break;
            case "all":
                Arrays.fill(set, true);
                break;
            case "empty":
                Arrays.fill(set, false);
                break;
            }
        }
         System.out.println(ans);
    }
}

[BOJ] 1987. 알파벳 (Java)

Problem

제출일 : 2020-02-14

문제 풀이 시간 : 30M

난이도 : ★★


link : https://www.acmicpc.net/problem/1987

세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다.

말은 상하좌우로 인접한 네 칸 중의 한 칸으로 이동할 수 있는데, 새로 이동한 칸에 적혀 있는 알파벳은 지금까지 지나온 모든 칸에 적혀 있는 알파벳과는 달라야 한다. 즉, 같은 알파벳이 적힌 칸을 두 번 지날 수 없다.

좌측 상단에서 시작해서, 말이 최대한 몇 칸을 지날 수 있는지를 구하는 프로그램을 작성하시오. 말이 지나는 칸은 좌측 상단의 칸도 포함된다.

input

첫째 줄에 R과 C가 빈칸을 사이에 두고 주어진다. (1<=R,C<=20) 둘째 줄부터 R개의 줄에 걸쳐서 보드에 적혀 있는 C개의 대문자 알파벳들이 빈칸 없이 주어진다.

Output

첫째 줄에 말이 지날 수 있는 최대의 칸 수를 출력한다.

Example

input

2 4
CAAB
ADCB

output

3

Solution & Inpression

처음 문제를 읽었을때 DFS, BFS 어느 방법이든 문제를 해결할 수 있을것이라 생각했다.

하지만 BFS로는 백트레킹을 구현하기가 어려운 부분이 있어 DFS로 문제를 해결했고

답이 될 수 있는 최대값은 26으로 26이 넘는다면 더이상 탐색을 하지 않고 종료하는 것으로 시간을 줄일 수 있었다.

Code

언어 : JAVA

메모리 : 15,140 kb

실행시간 : 940 ms

import java.util.Scanner;

public class Main {
    static int R, C, ans;
    static char[][] map;
    static boolean[] visit = new boolean[26];
    static int[] dr = { -1, 1, 0, 0 };
    static int[] dc = { 0, 0, -1, 1 };

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

        R = sc.nextInt();
        C = sc.nextInt();
        map = new char[R][C];
        for (int i = 0; i < R; i++) {
            String str = sc.next();
            for (int j = 0; j < C; j++) {
                map[i][j] = str.charAt(j);
            }
        }
        ans = 0;
        visit[map[0][0] - 'A'] = true;
        DFS(0, 0, 1);
        System.out.println(ans);
    }

    private static void DFS(int i, int j, int cnt) {
        if (ans < cnt)
            ans = cnt;
        else if (ans == 26)
            return;
        for (int dir = 0; dir < dr.length; dir++) {
            int nr = i + dr[dir];
            int nc = j + dc[dir];

            if (isRange(nr, nc) && !visit[map[nr][nc] - 'A']) {
                visit[map[nr][nc] - 'A'] = true;
                DFS(nr, nc, cnt + 1);
                visit[map[nr][nc] - 'A'] = false;
            }
        }

    }

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

[BOJ] 2422. 한윤정이 이탈리아에 가서 아이스크림을 사먹는데 - BF (Java)

Problem

제출일 : 2020-02-13

문제 풀이 시간 : 15M

난이도 : ☆


link : https://www.acmicpc.net/problem/2422

한윤정과 친구들은 이탈리아로 방학 여행을 갔다. 이탈리아는 덥다. 윤정이와 친구들은 아이스크림을 사먹기로 했다. 아이스크림 가게에는 N종류의 아이스크림이 있다. 모든 아이스크림은 1부터 N까지 번호가 매겨져있다. 어떤 종류의 아이스크림을 함께먹으면, 맛이 아주 형편없어진다. 따라서 윤정이는 이러한 경우를 피하면서 아이스크림을 3가지 선택하려고 한다. 이때, 선택하는 방법이 몇 가지인지 구하려고 한다.

input

첫째 줄에 정수 N과 M이 주어진다. N은 아이스크림 종류의 수이고, M은 섞어먹으면 안 되는 조합의 개수이다. 아래 M개의 줄에는 섞어먹으면 안 되는 조합의 번호가 주어진다. 같은 조합은 두 번 이상 나오지 않는다. (1 ≤ N ≤ 200, 0 ≤ M ≤ 10,000)

Output

첫째 줄에, 가능한 방법이 총 몇 개 있는지 출력한다.

Example

input

5 3
1 2
3 4
1 3

output

3

Solution & Inpression

순열을 이용하여 경우의 수를 구하고 금지된 조합일 경우 카운트를 하지 않는다.

Code

언어 : JAVA

메모리 : 34,032 kb

실행시간 : 104 ms

import java.util.Scanner;

public class Main {
    static int N, M, ans;
    static boolean[][] graph;
    static boolean[] visit;
    static int[] data = new int[3];

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

        N = sc.nextInt();
        M = sc.nextInt();

        graph = new boolean[N][N];
        visit = new boolean[N];

        for (int i = 0; i < M; i++) {
            int s = sc.nextInt() - 1;
            int e = sc.nextInt() - 1;
            graph[s][e] = true;
            graph[e][s] = true;
        }
        ans = 0;
        solve(0, 0);
        System.out.println(ans);

    }

    private static void solve(int index, int depth) {
        if (depth == 3) {
            if (check())
                ans++;
            return;
        }
        for (int i = index; i < N; i++) {
            if (!visit[i]) {
                visit[i] = true;
                data[depth] = i;
                solve(i, depth + 1);
                visit[i] = false;
            }
        }
    }

    private static boolean check() {
        for (int i = 0; i < 2; i++) {
            for (int j = i + 1; j < 3; j++) {
                if (graph[data[i]][data[j]])
                    return false;
            }
        }
        return true;
    }
}

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

[BOJ] 11723. 집합 - 비트마스킹 (Java)  (0) 2020.02.16
[BOJ] 1987. 알파벳 (Java)  (2) 2020.02.14
[BOJ] 4963. 섬의 개수 - DFS  (0) 2020.02.14
[BOJ] 1107. 리모컨 - BF  (0) 2020.02.14
[BOJ] 9944. NxM 보드 완주하기  (0) 2020.02.13

[BOJ] 4963. 섬의 개수 - DFS

Problem

제출일 : 2020-02-13

문제 풀이 시간 : 20M

난이도 : ★


link : https://www.acmicpc.net/problem/4963

정사각형으로 이루어져 있는 섬과 바다 지도가 주어진다. 섬의 개수를 세는 프로그램을 작성하시오.

img

한 정사각형과 가로, 세로 또는 대각선으로 연결되어 있는 사각형은 걸어갈 수 있는 사각형이다.

두 정사각형이 같은 섬에 있으려면, 한 정사각형에서 다른 정사각형으로 걸어서 갈 수 있는 경로가 있어야 한다. 지도는 바다로 둘러쌓여 있으며, 지도 밖으로 나갈 수 없다.

input

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 지도의 너비 w와 높이 h가 주어진다. w와 h는 50보다 작거나 같은 양의 정수이다.

둘째 줄부터 h개 줄에는 지도가 주어진다. 1은 땅, 0은 바다이다.

입력의 마지막 줄에는 0이 두 개 주어진다.

Output

각 테스트 케이스에 대해서, 섬의 개수를 출력한다.

Example

input

1 1
0
2 2
0 1
1 0
3 2
1 1 1
1 1 1
5 4
1 0 1 0 0
1 0 0 0 0
1 0 1 0 1
1 0 0 1 0
5 4
1 1 1 0 1
1 0 1 0 1
1 0 1 0 1
1 0 1 1 1
5 5
1 0 1 0 1
0 0 0 0 0
1 0 1 0 1
0 0 0 0 0
1 0 1 0 1
0 0

output

0
1
1
3
1
9

Solution & Inpression

기본적인 DFS/BFS문제로 DFS나 BFS어떤 풀이를 사용하더라도 문제는 해결할 수 있다.

어렵거나 문제가 이해가 되지 않았던 부분이 없었기 때문에 쉽게 풀 수 있었다

단지 높이와 너비를 입력으로 주는 순서나 0 0이 종료조건이며 대각선 방향도 연결이 되있다는것을 파악하면 한번에 해결할 수 있을 것이라 생각한다.

Code

언어 : JAVA

메모리 : 32,616 kb

실행시간 : 312 ms

import java.io.FileInputStream;
import java.util.Scanner;

public class Main {
    static int W, H, ans;
    static int[][] map;
    // 상, 하, 좌, 우, 좌상, 좌하, 우상, 우하
    static int[] dr = { -1, 1, 0, 0, -1, 1, -1, 1 };
    static int[] dc = { 0, 0, -1, 1, -1, -1, 1, 1 };

    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream("res/input.txt"));
        Scanner sc = new Scanner(System.in);

        while (true) {
            W = sc.nextInt();
            if (W == 0)
                break;
            H = sc.nextInt();

            map = new int[H][W];

            for (int i = 0; i < H; i++) {
                for (int j = 0; j < W; j++) {
                    map[i][j] = sc.nextInt();
                }
            }
            ans = 0;
            for (int i = 0; i < H; i++) {
                for (int j = 0; j < W; j++) {
                    if (map[i][j] == 1) {
                        DFS(i, j);
                        ans++;

                    }
                }
            }
            System.out.println(ans);
        } // end of while

    }

    private static void DFS(int i, int j) {
        for (int dir = 0; dir < dc.length; dir++) {
            int nr = i + dr[dir];
            int nc = j + dc[dir];
            if (isRange(nr, nc) && map[nr][nc] == 1) {
                map[nr][nc] = 0;
                DFS(nr, nc);
            }
        }
    }

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

[BOJ] 1107. 리모컨 - BF

Problem

제출일 : 2020-02-13

문제 풀이 시간 : 2H

난이도 : ★★★☆


link : https://www.acmicpc.net/problem/1107

수빈이는 TV를 보고 있다. 수빈이는 채널을 돌리려고 했지만, 버튼을 너무 세게 누르는 바람에, 일부 숫자 버튼이 고장났다.

리모컨에는 버튼이 0부터 9까지 숫자, +와 -가 있다. +를 누르면 현재 보고있는 채널에서 +1된 채널로 이동하고, -를 누르면 -1된 채널로 이동한다. 채널 0에서 -를 누른 경우에는 채널이 변하지 않고, 채널은 무한대 만큼 있다.

수빈이가 지금 이동하려고 하는 채널은 N이다. 어떤 버튼이 고장났는지 주어졌을 때, 채널 N으로 이동하기 위해서 버튼을 최소 몇 번 눌러야하는지 구하는 프로그램을 작성하시오.

수빈이가 지금 보고 있는 채널은 100번이다.

input

첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼이 주어지며, 같은 버튼이 여러 번 주어지는 경우는 없다.

Output

첫째 줄에 채널 N으로 이동하기 위해 버튼을 최소 몇 번 눌러야 하는지를 출력한다.

Example

input

500000
8
0 2 3 4 6 7 8 9

output

11117

Solution & Inpression

3번을 다시 풀어 통과한 문제

처음 문제를 접근할때 goal에 가장 근접한 값을 찾아 계산하려 했지만 생각보다 예외가 많아 완전 탐색을 수행했다.

입력으로 주어지는 최대 값은 500,000 으로 6자리의 숫자이기때문에 0~999999을 탐색을 수행했다.

2시간이 넘게 붙잡고 문제를 풀었지만 쉽게 푸려니 한없이 쉽게 풀어지는 문제라고 생각이 든다.

Code1

언어 : JAVA

메모리 : 109,160 kb

실행시간 : 384 ms

import java.io.FileInputStream;
import java.util.Scanner;

public class Main {
    static boolean[] broken;

    public static void main(String[] args) throws Exception {
        // System.setIn(new FileInputStream("res/input.txt"));
        Scanner sc = new Scanner(System.in);
        int start = 100;
        int goal = sc.nextInt();
        if (start == goal) {
            System.out.println(0);
            return;
        }
        int N = sc.nextInt();
        broken = new boolean[10];
        for (int i = 0; i < N; i++) {
            broken[sc.nextInt()] = true;
        }
        int ans = Math.abs(goal - start);
        for (int i = 0; i <= 1000000; i++) { // 숫자 버튼으로 이동하는 채널
            if (go(i)) {
                int tmp = Math.abs(i - goal) + ("" + i).length(); // +나 -를 몇 번 눌러야 하는지 계산
                if (ans > tmp) {
                    ans = tmp;
                }
            }
        }
        System.out.println(ans);
    }

    private static boolean go(int x) {
        String str = "" + x;
        for (int i = 0; i < str.length(); i++) {
            if (broken[str.charAt(i) - '0'])
                return false;
        }
        return true;
    }
}

[BOJ] 9944. NxM 보드 완주하기

Problem

제출일 : 2020-02-13

문제 풀이 시간 : 1H 30M

난이도 : ★★★☆


link : https://www.acmicpc.net/problem/9944

N×M 보드 위에서 할 수 있는 게임이 있다. 보드는 크기가 1×1인 정사각형 칸으로 나누어져 있다. 보드의 각 칸은 빈 칸 또는 장애물이다. 장애물은 아래 그림에선 어두운 사각형으로 표시되어져 있다.

게임을 시작하려면 보드의 빈 칸 위에 공을 하나 놓아야 한다. 아래 그림에서 공은 회색 점으로 표시되어져 있다. 게임은 단계로 이루어져 있고, 각 단계는 아래와 같이 구성되어져 있다.

  • 위, 아래, 오른쪽, 왼쪽 중 방향 하나를 고른 다음, 그 방향으로 공을 계속해서 이동시킨다.
  • 공은 장애물, 보드의 경계, 이미 공이 지나갔던 칸을 만나기 전까지 계속해서 이동한다.

게임은 공이 더 이상 이동할 수 없을 때 끝난다. 이 때, 모든 빈 칸을 공이 방문한 적이 있어야 한다.

아래 그림은 총 10단계 만에 모든 칸을 방문하는 방법이다.

img

보드의 상태가 주어졌을 때, 모든 칸을 방문하기 위한 이동 횟수의 최솟값을 구하는 프로그램을 작성하시오.

Input

입력은 여러 개의 테스트 케이스로 이루어져 있다.

각 테스트 케이스의 첫째 줄에는 보드의 크기를 나타내는 N과 M이 주어진다. N은 세로 크기, M은 가로 크기이고, 두 값은 30보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 상태가 주어진다. 보드의 상태는 장애물을 나타내는 '*'과 빈 칸을 나타내는 '.'으로 이루어져 있다.

입력으로 주어진 보드가 장애물로만 이루어진 경우는 없다.

Output

각 테스트 케이스마다 보드의 모든 빈 칸을 방문하는 최소 이동 횟수를 출력한다. 출력 형식은 예제를 참고한다.

만약, 모든 빈 칸을 방문할 수 없다면 최소 이동 횟수는 -1이다.

Example

input

5 5
**...
.....
....*
..*..
.....
3 4
****
*...
*..*

output

Case 1: 10
Case 2: 3

Solution & Inpression

색다른 DFS문제 + 입력의 개수가 정해지지 않았기 때문에 EOF 처리로 종료

출력 부분 대소문자와 띄어쓰기를 유의하여야 한다. >> 이부분을 찾느라 1시간이 넘게 걸림.....

Code

언어 : JAVA

메모리 : 20,148 kb

실행시간 : 356 ms

import java.util.Scanner;

public class Main {
    static int N, M, ans;
    static int[][] map;
    // 상 하 좌 우
    static int[] dr = { -1, 1, 0, 0 };
    static int[] dc = { 0, 0, -1, 1 };

    public static void main(String[] args) throws Exception {
        //System.setIn(new FileInputStream("res/input.txt"));
        Scanner sc = new Scanner(System.in);
        int TC = 0;
        while (sc.hasNext()) {
            N = sc.nextInt();
            M = sc.nextInt();
            map = new int[N][M];
            int blank = -1; // 빈 칸의 개수
            for (int i = 0; i < N; i++) {
                String str = sc.next();
                for (int j = 0; j < M; j++) {
                    if (str.charAt(j) == '*')
                        map[i][j] = 1;
                    else {
                        map[i][j] = 0;
                        blank++;
                    }
                }
            }

            ans = 123456789;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < M; j++) {
                    if (map[i][j] == 0) {
                        map[i][j] = 1;
                        DFS(i, j, 0, blank);
                        map[i][j] = 0;
                    }
                }
            }
            if (ans == 123456789)
                ans = -1;
            System.out.println("Case " + ++TC + ": " + ans);
        }
    }

    private static void DFS(int i, int j, int cnt, int blank) {
        if (blank == 0 && ans > cnt) {
            ans = cnt;
            return;
        }

        for (int dir = 0; dir < 4; dir++) {
            int nr = i + dr[dir];
            int nc = j + dc[dir];
            if (isRange(nr, nc) && map[nr][nc] == 0) {
                while (isRange(nr, nc) && map[nr][nc] == 0) {
                    map[nr][nc] = 1;
                    blank--;
                    nr += dr[dir];
                    nc += dc[dir];
                }
                nr -= dr[dir];
                nc -= dc[dir];
                DFS(nr, nc, cnt + 1, blank);

                while (nr != i || nc != j) {
                    map[nr][nc] = 0;
                    blank++;
                    nr -= dr[dir];
                    nc -= dc[dir];
                }
            }

        }
    }

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

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

[BOJ] 4963. 섬의 개수 - DFS  (0) 2020.02.14
[BOJ] 1107. 리모컨 - BF  (0) 2020.02.14
[BOJ] 16985. Maaaaaaaaaze - Graph/BF/BFS  (0) 2020.02.12
[BOJ] 14501. 퇴사/15486. 퇴사2 - BF/DP  (0) 2020.02.06
[BOJ] 13023. ABCDE - Graph  (2) 2020.02.03

[BOJ] 16985. Maaaaaaaaaze - Graph/BF/BFS

Problem

제출일 : 2020-02-11

문제 풀이 시간 : 2H

난이도 : ★★★☆


link : https://www.acmicpc.net/problem/16985

평화롭게 문제를 경작하며 생활하는 BOJ 마을 사람들은 더 이상 2차원 미로에 흥미를 느끼지 않는다. 2차원 미로는 너무나 쉽게 탈출이 가능하기 때문이다. 미로를 이 세상 그 누구보다 사랑하는 준현이는 이런 상황을 매우 안타깝게 여겨 아주 큰 상금을 걸고 BOJ 마을 사람들의 관심을 확 끌 수 있는 3차원 미로 탈출 대회를 개최하기로 했다.

대회의 규칙은 아래와 같다.

  • 5×5 크기의 판이 5개 주어진다. 이중 일부 칸은 참가자가 들어갈 수 있고 일부 칸은 참가자가 들어갈 수 없다. 그림에서 하얀 칸은 참가자가 들어갈 수 있는 칸을, 검은 칸은 참가자가 들어갈 수 없는 칸을 의미한다.

img

  • 참가자는 주어진 판들을 시계 방향, 혹은 반시계 방향으로 자유롭게 회전할 수 있다. 그러나 판을 뒤집을 수는 없다.

img

  • 회전을 완료한 후 참가자는 판 5개를 쌓는다. 판을 쌓는 순서는 참가자가 자유롭게 정할 수 있다. 이렇게 판 5개를 쌓아 만들어진 5×5×5 크기의 큐브가 바로 참가자를 위한 미로이다. 이 때 큐브의 입구는 정육면체에서 참가자가 임의로 선택한 꼭짓점에 위치한 칸이고 출구는 입구와 면을 공유하지 않는 꼭짓점에 위치한 칸이다.

img

  • 참가자는 현재 위치한 칸에서 면으로 인접한 칸이 참가자가 들어갈 수 있는 칸인 경우 그 칸으로 이동할 수 있다.
  • 참가자 중에서 본인이 설계한 미로를 가장 적은 이동 횟수로 탈출한 사람이 우승한다. 만약 미로의 입구 혹은 출구가 막혀있거나, 입구에서 출구에 도달할 수 있는 방법이 존재하지 않을 경우에는 탈출이 불가능한 것으로 간주한다.

이 대회에서 우승하기 위해서는 미로를 잘 빠져나올 수 있기 위한 담력 증진과 체력 훈련, 그리고 적절한 운이 제일 중요하지만, 가장 적은 이동 횟수로 출구에 도달할 수 있게끔 미로를 만드는 능력 또한 없어서는 안 된다. 주어진 판에서 가장 적은 이동 횟수로 출구에 도달할 수 있게끔 미로를 만들었을 때 몇 번 이동을 해야하는지 구해보자.

input

첫째 줄부터 25줄에 걸쳐 판이 주어진다. 각 판은 5줄에 걸쳐 주어지며 각 줄에는 5개의 숫자가 빈칸을 사이에 두고 주어진다. 0은 참가자가 들어갈 수 없는 칸, 1은 참가자가 들어갈 수 있는 칸을 의미한다.

Output

첫째 줄에 주어진 판으로 설계된 미로를 탈출하는 가장 적은 이동 횟수를 출력한다. 단, 어떻게 설계하더라도 탈출이 불가능할 경우에는 -1을 출력한다.

Example

input

1 1 0 0 0
0 0 0 0 1
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 1 1 1
1 0 0 0 0
0 0 1 0 0
0 0 1 1 1
0 0 1 0 0
0 0 0 0 0
0 0 1 0 1
0 0 0 0 0
0 0 0 1 0
0 0 1 0 1
0 0 1 0 0
1 0 0 0 0
0 0 1 1 0
1 0 1 0 0
0 0 1 0 1
0 0 1 1 0
1 1 0 1 1
0 0 0 0 1
0 1 0 1 0
0 1 0 0 0

output

16

Solution & Inpression

위 문제는 $5 \times 5$의 판 $5$개가 주어진다.

각각의 판을 회전하고 층을 바꾸어 미로를 구성할 수 있다. 미로를 탈출할때 가장 적은 이동횟수를 출력하면 된다.

각각의 판의 층이 바뀌는 경우의 수는 $5!$ 이며 각 판은 $0,90,180,270$도씩 회전해 4개의 상태를 갖을수 있다,

따라서 생성가능한 미로의 수는
$$
5! \times 4^5 =75,000‬
$$
개의 미로를 생성할 수 있다

생성된 미로를 BFS로 탐색했다 3차원의 x,y,z축에서 각각 +1, -1로 한 칸씩 6방향으로 이동할 수 있다.


순열과 중복순열을 이용해 미로의 경우를 구했고 큐를 이용하여 너비우선 탐색을 구현했다.

Code

언어 : JAVA

메모리 : 279,664 kb

실행시간 : 1,176 ms

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

public class Main {
    static int[][][][] map = new int[5][4][5][5];
    static int[] solData = new int[5], goData = new int[5];
    static boolean[] visit = new boolean[5];
    static int ans;
//상하좌우 아래 위
    static int[] dx = { 0, 0, -1, 1, 0, 0 };
    static int[] dy = { -1, 1, 0, 0, 0, 0 };
    static int[] dz = { 0, 0, 0, 0, 1, -1 };

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int k = 0; k < 5; k++) {
            for (int i = 0; i < 5; i++) {
                for (int j = 0; j < 5; j++) {
                    map[k][0][i][j] = sc.nextInt();
                }
            }
            rotateMap(k);
        }

        ans = Integer.MAX_VALUE;

        solve(0); // 각 판의 회전 정도를 결정

        if (ans == Integer.MAX_VALUE)
            System.out.println(-1);
        else
            System.out.println(ans);
    }

    private static void solve(int depth) {
        if (depth == 5) {
            go(0); // 판의 순서를 결정
            return;
        }
        for (int i = 0; i < 4; i++) {
            solData[depth] = i;
            solve(depth + 1);
        }
    }

    private static void go(int depth) {
        if (depth == 5) {
            calc();
            return;
        }
        for (int i = 0; i < 5; i++) {
            if (!visit[i]) {
                visit[i] = true;
                goData[depth] = i;
                go(depth + 1);
                visit[i] = false;
            }
        }
    }

    private static void calc() {
        int[][][] copy = new int[5][5][5];
        for (int k = 0; k < 5; k++) {
            for (int i = 0; i < 5; i++) {
                for (int j = 0; j < 5; j++) {
                    copy[k][i][j] = map[goData[k]][solData[k]][i][j];
                }
            }
        }
        // BFS
        // 입구나 출구가 없는 경우
        if (copy[0][0][0] != 1 || copy[4][4][4] != 1)
            return;

        Queue<int[]> q = new LinkedList<>();
        boolean visit[][][] = new boolean[5][5][5];
        q.offer(new int[] { 0, 0, 0, 0 });
        visit[0][0][0] = true;

        while (!q.isEmpty()) {
            int[] cur = q.poll();
            // 최저값 보다 클 경우 종료
            if (cur[3] >= ans)
                return;
            // 도착 종료
            if (cur[0] == 4 && cur[1] == 4 && cur[2] == 4) {
                if (ans > cur[3])
                    ans = cur[3];
                return;
            }
            for (int dir = 0; dir < 6; dir++) {
                int nz = cur[0] + dz[dir];
                int ny = cur[1] + dy[dir];
                int nx = cur[2] + dx[dir];
                if (isRange(nz, ny, nx) && !visit[nz][ny][nx] && copy[nz][ny][nx] == 1) {
                    q.offer(new int[] { nz, ny, nx, cur[3] + 1 });
                    visit[nz][ny][nx] = true;
                }
            }
        }
    }

    private static boolean isRange(int nz, int ny, int nx) {
        if (0 <= nz && nz < 5 && 0 <= ny && ny < 5 && 0 <= nx && nx < 5)
            return true;
        return false;
    }

    private static void rotateMap(int k) {
        for (int turn = 1; turn < 4; turn++) {
            for (int i = 0; i < 5; i++) {
                for (int j = 0; j < 5; j++) {
                    map[k][turn][j][4 - i] = map[k][turn - 1][i][j];
                }
            }
        }
    }
}

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

[BOJ] 1107. 리모컨 - BF  (0) 2020.02.14
[BOJ] 9944. NxM 보드 완주하기  (0) 2020.02.13
[BOJ] 14501. 퇴사/15486. 퇴사2 - BF/DP  (0) 2020.02.06
[BOJ] 13023. ABCDE - Graph  (2) 2020.02.03
[BOJ] 1600. 말이 되고픈 원숭이 - BFS  (0) 2019.11.12