[BOJ] 2003. 수들의 합 2 - (Java)

Problem

제출일 : 2020-03-15

문제 풀이 시간 : 20M

난이도 : ★


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

N개의 수로 된 수열 A[1], A[2], …, A[N] 이 있다. 이 수열의 i번째 수부터 j번째 수까지의 합 A[i]+A[i+1]+…+A[j-1]+A[j]가 M이 되는 경우의 수를 구하는 프로그램을 작성하시오.

Input

첫째 줄에 N(1≤N≤10,000), M(1≤M≤300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.

Output

첫째 줄에 경우의 수를 출력한다.

Example

input

4 2
1 1 1 1

output

3

Solution & Inpression

완전탐색? 포인터의 활용..

code 1 : 처음 문제를 풀 때 한번에 더할 개수를 정하고 그 수만큼 더한 모든 경우의 수를 탐색했다.

code 2 : code 1의 최적화 버전으로 양쪽에 포인터를 두어 목표 값보다 작다면 값을 더하고 작으면 값을 빼는 방법으로 굳이 검사하지 않아도 되는 부분을 검사하지 않을 수 있어 속도가 더욱 빠른 코드이다.

Code 1

언어 : JAVA

메모리 : 26960 kb

실행 시간 : 440 ms

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

public class Main {
    static int N, M, ans;
    static int[] data;

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

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

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

        ans = 0;
        for (int i = 1; i <= N; i++) {
            solve(i);
        }
        System.out.println(ans);
    }

    private static void solve(int size) {
        int sum = 0;
        for (int i = 0; i < size; i++) {
            sum += data[i];
        }

        for (int i = size; i < N; i++) {
            if (sum == M)
                ans++;
            sum += data[i];
            sum -= data[i - size];
        }
        if (sum == M)
            ans++;
    }
}

Code 2

언어 : JAVA

메모리 : 27320 kb

실행 시간 : 244 ms

import java.util.Scanner;

public class Main {

    static int N, M, ans, sum;
    static int[] data;
    static int s, e;

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

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

        data = new int[N + 1];
        for (int i = 0; i < N; i++) {
            data[i] = sc.nextInt();
        }
        s = 0;
        e = 0;

        while (e <= N) {
            if (sum == M)
                ans++;

            if (sum < M)
                sum += data[e++];
            else
                sum -= data[s++];
        }

        System.out.println(ans);

    }

}

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

[BOJ] 1208. 부분수열의 합 2 - (Java)  (0) 2020.03.16
[BOJ] 1806. 부분합 - (Java)  (0) 2020.03.15
[BOJ] 12100. 2048 (Easy) - (Java)  (0) 2020.03.15
[BOJ] 13460. 구슬 탈출 2 (Java)  (1) 2020.03.14
[BOJ] 1062. 가르침 (Java)  (0) 2020.03.14

[BOJ] 12100. 2048 (Easy) - (Java)

Problem

제출일 : 2020-03-15

문제 풀이 시간 : 1H

난이도 : ★★★


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

2048 게임은 4×4 크기의 보드에서 혼자 즐기는 재미있는 게임이다. 이 링크를 누르면 게임을 해볼 수 있다.

이 게임에서 한 번의 이동은 보드 위에 있는 전체 블록을 상하좌우 네 방향 중 하나로 이동시키는 것이다. 이때, 같은 값을 갖는 두 블록이 충돌하면 두 블록은 하나로 합쳐지게 된다. 한 번의 이동에서 이미 합쳐진 블록은 또 다른 블록과 다시 합쳐질 수 없다. (실제 게임에서는 이동을 한 번 할 때마다 블록이 추가되지만, 이 문제에서 블록이 추가되는 경우는 없다)

img img img
<그림 1> <그림 2> <그림 3>

<그림 1>의 경우에서 위로 블록을 이동시키면 <그림 2>의 상태가 된다. 여기서, 왼쪽으로 블록을 이동시키면 <그림 3>의 상태가 된다.

img img img img
<그림 4> <그림 5> <그림 6> <그림 7>

<그림 4>의 상태에서 블록을 오른쪽으로 이동시키면 <그림 5>가 되고, 여기서 다시 위로 블록을 이동시키면 <그림 6>이 된다. 여기서 오른쪽으로 블록을 이동시켜 <그림 7>을 만들 수 있다.

img img
<그림 8> <그림 9>

<그림 8>의 상태에서 왼쪽으로 블록을 옮기면 어떻게 될까? 2가 충돌하기 때문에, 4로 합쳐지게 되고 <그림 9>의 상태가 된다.

img img img img
<그림 10> <그림 11> <그림 12> <그림 13>

<그림 10>에서 위로 블록을 이동시키면 <그림 11>의 상태가 된다.

<그림 12>의 경우에 위로 블록을 이동시키면 <그림 13>의 상태가 되는데, 그 이유는 한 번의 이동에서 이미 합쳐진 블록은 또 합쳐질 수 없기 때문이다.

img img
<그림 14> <그림 15>

마지막으로, 똑같은 수가 세 개가 있는 경우에는 이동하려고 하는 쪽의 칸이 먼저 합쳐진다. 예를 들어, 위로 이동시키는 경우에는 위쪽에 있는 블록이 먼저 합쳐지게 된다. <그림 14>의 경우에 위로 이동하면 <그림 15>를 만든다.

이 문제에서 다루는 2048 게임은 보드의 크기가 N×N 이다. 보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 5번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하는 프로그램을 작성하시오.

Input

첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2보다 크거나 같고, 1024보다 작거나 같은 2의 제곱꼴이다. 블록은 적어도 하나 주어진다.

Output

최대 5번 이동시켜서 얻을 수 있는 가장 큰 블록을 출력한다.

Example

input

3
2 2 2
4 4 4
8 8 8

output

16

Solution & Inpression

시뮬레이션 + 완전 탐색

모든 경우의 수를 탐색하는 완전 탐색 시뮬레이션 문제

인덱스 조작이 까다로워 ArrayList를 사용하여 문제를 해결하였다. 움직이는 방향에 따라 list에 값을 넣는 순서를 바꾸어 작업했다.

list[i].get(0).equals(list[i].get(1))이렇게 두 수가 합쳐질 수 있는지 검사하였는데 처음 코드는

list[i].get(0)==list[i].get(1)이었는데 왜 인지 모르게 틀렸다. 왜 지?????

Code

언어 : JAVA

메모리 : 44464 kb

실행 시간 : 736 ms

import java.util.ArrayList;
import java.util.Scanner;

public class Main {
    static int N, ans;
    static int[][] map;
    static int[] data;
    static ArrayList<Integer>[] list;

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

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                map[i][j] = sc.nextInt();
            }
        }
        ans = 0;
        data = new int[5];

        list = new ArrayList[N];
        for (int i = 0; i < N; i++) {
            list[i] = new ArrayList<>();
        }
        solve(0, 0);
        System.out.println(ans);

    }

    private static void solve(int index, int depth) {
        if (depth == 5) {
            check();
            return;
        }
        for (int i = 0; i < 4; i++) {
            if (data[depth] == 0) {
                data[depth] = i;
                solve(index, depth + 1);
                data[depth] = 0;
            }
        }
    }

    private static void check() {
        int[][] copy = new int[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                copy[i][j] = map[i][j];
            }
        }

        for (int time = 0; time < 5; time++) {
            switch (data[time]) {
            case 0: // 상
                for (int r = 0; r < N; r++) {
                    for (int c = 0; c < N; c++) {
                        if (copy[r][c] != 0) {
                            list[c].add(copy[r][c]);
                            copy[r][c] = 0;
                        }
                    }
                }
                for (int i = 0; i < N; i++) {
                    int r = 0;
                    while (list[i].size() != 0) {
                        if (list[i].size() != 1 && list[i].get(0).equals(list[i].get(1))) {
                            copy[r++][i] = list[i].get(0) << 1;
                            list[i].remove(0);
                            list[i].remove(0);
                        } else {
                            copy[r++][i] = list[i].get(0);
                            list[i].remove(0);
                        }
                    }
                }
                break;

            case 1: // 하
                for (int r = N - 1; r >= 0; r--) {
                    for (int c = 0; c < N; c++) {
                        if (copy[r][c] != 0) {
                            list[c].add(copy[r][c]);
                            copy[r][c] = 0;
                        }
                    }
                }

                for (int i = 0; i < N; i++) {
                    int r = N - 1;
                    while (list[i].size() != 0) {
                        if (list[i].size() != 1 && list[i].get(0).equals(list[i].get(1))) {
                            copy[r--][i] = list[i].get(0) << 1;
                            list[i].remove(0);
                            list[i].remove(0);
                        } else {
                            copy[r--][i] = list[i].get(0);
                            list[i].remove(0);
                        }
                    }
                }
                break;
            case 2: // 좌
                for (int r = 0; r < N; r++) {
                    for (int c = 0; c < N; c++) {
                        if (copy[r][c] != 0) {
                            list[r].add(copy[r][c]);
                            copy[r][c] = 0;
                        }
                    }
                }

                for (int i = 0; i < N; i++) {
                    int c = 0;
                    while (list[i].size() != 0) {
                        if (list[i].size() != 1 && list[i].get(0).equals(list[i].get(1))) {
                            copy[i][c++] = list[i].get(0) << 1;
                            list[i].remove(0);
                            list[i].remove(0);
                        } else {
                            copy[i][c++] = list[i].get(0);
                            list[i].remove(0);
                        }
                    }
                }
                break;
            case 3: // 우
                for (int r = 0; r < N; r++) {
                    for (int c = N - 1; c >= 0; c--) {
                        if (copy[r][c] != 0) {
                            list[r].add(copy[r][c]);
                            copy[r][c] = 0;
                        }
                    }
                }

                for (int i = 0; i < N; i++) {
                    int c = N - 1;
                    while (list[i].size() != 0) {
                        if (list[i].size() != 1 && list[i].get(0).equals(list[i].get(1))) {
                            copy[i][c--] = list[i].get(0) << 1;
                            list[i].remove(0);
                            list[i].remove(0);
                        } else {
                            copy[i][c--] = list[i].get(0);
                            list[i].remove(0);
                        }
                    }
                }
                break;
            }// end of switch
        }

        int max = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (max < copy[i][j])
                    max = copy[i][j];
            }
        }

        if (ans < max)
            ans = max;

    }
}

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

[BOJ] 1806. 부분합 - (Java)  (0) 2020.03.15
[BOJ] 2003. 수들의 합 2 - (Java)  (2) 2020.03.15
[BOJ] 13460. 구슬 탈출 2 (Java)  (1) 2020.03.14
[BOJ] 1062. 가르침 (Java)  (0) 2020.03.14
[BOJ] 14391. 종이 조각 (Java)  (0) 2020.03.14

[BOJ] 13460. 구슬 탈출 2 (Java)

Problem

제출일 : 2020-03-14

문제 풀이 시간 : 2H 20M

난이도 : ★★★★☆


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

스타트링크에서 판매하는 어린이용 장난감 중에서 가장 인기가 많은 제품은 구슬 탈출이다. 구슬 탈출은 직사각형 보드에 빨간 구슬과 파란 구슬을 하나씩 넣은 다음, 빨간 구슬을 구멍을 통해 빼내는 게임이다.

보드의 세로 크기는 N, 가로 크기는 M이고, 편의상 1×1크기의 칸으로 나누어져 있다. 가장 바깥 행과 열은 모두 막혀져 있고, 보드에는 구멍이 하나 있다. 빨간 구슬과 파란 구슬의 크기는 보드에서 1×1크기의 칸을 가득 채우는 사이즈이고, 각각 하나씩 들어가 있다. 게임의 목표는 빨간 구슬을 구멍을 통해서 빼내는 것이다. 이때, 파란 구슬이 구멍에 들어가면 안 된다.

이때, 구슬을 손으로 건드릴 수는 없고, 중력을 이용해서 이리 저리 굴려야 한다. 왼쪽으로 기울이기, 오른쪽으로 기울이기, 위쪽으로 기울이기, 아래쪽으로 기울이기와 같은 네 가지 동작이 가능하다.

각각의 동작에서 공은 동시에 움직인다. 빨간 구슬이 구멍에 빠지면 성공이지만, 파란 구슬이 구멍에 빠지면 실패이다. 빨간 구슬과 파란 구슬이 동시에 구멍에 빠져도 실패이다. 빨간 구슬과 파란 구슬은 동시에 같은 칸에 있을 수 없다. 또, 빨간 구슬과 파란 구슬의 크기는 한 칸을 모두 차지한다. 기울이는 동작을 그만하는 것은 더 이상 구슬이 움직이지 않을 때 까지이다.

보드의 상태가 주어졌을 때, 최소 몇 번 만에 빨간 구슬을 구멍을 통해 빼낼 수 있는지 구하는 프로그램을 작성하시오.

Input

첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B' 로 이루어져 있다. '.'은 빈 칸을 의미하고, '#'은 공이 이동할 수 없는 장애물 또는 벽을 의미하며, 'O'는 구멍의 위치를 의미한다. 'R'은 빨간 구슬의 위치, 'B'는 파란 구슬의 위치이다.

입력되는 모든 보드의 가장자리에는 모두 '#'이 있다. 구멍의 개수는 한 개 이며, 빨간 구슬과 파란 구슬은 항상 1개가 주어진다.

Output

최소 몇 번 만에 빨간 구슬을 구멍을 통해 빼낼 수 있는지 출력한다. 만약, 10번 이하로 움직여서 빨간 구슬을 구멍을 통해 빼낼 수 없으면 -1을 출력한다.

Example

input

5 5
#####
#..B#
#.#.#
#RO.#
#####

output

1

Solution & Inpression

시뮬레이션 + 그래프 탐색(BFS)

시뮬레이션 문제도 자주 풀어봐야 하지만 구현의 아이디어가 어렵다 라기보단 구현이 까다로워 자주 풀지 못하는 문제 유형이다. 많은 연습이 필요한 유형이지만 풀기 싫은게 사실이다.

시뮬레이션 문제는 많은 테스트케이스가 주어진다면 쉽게 문제를 찾을지도 모르지만 보통 늪에 빠져버린다.

보통 최소값을 찾는 탐색 문제는 BFS로 접근을 하며 문제를 해결할 때 배열로 가지고 있으며 해결하는 것을 선호하지만 이번엔 클래스를 이용하여 구현해보았다.

움직이는 과정이 까다롭기 때문에 따로 배열을 만들어 움직여 주었다. 이때 현재 위치와 방향을 매개변수로 넘겨주는데 현재 위치를 클래스로 넘기기에 값을 참조 호출하기 때문에 값을 복사하여 사용해야 했다.

또 방문 처리를 어떻게 할까 고민해보았는데 아래 소스와 같이 4차원 배열을 이용하여 해결했다.

최적화를 위해 다른 사람들이 푼 소스를 보았는데 일단 움직이고 같으면 처리하는 방식으로 문제를 해결한 사람도 있었다. 이 방법이 더 깔끔한 방법이지 않을까 싶기도 하다.

Code

언어 : JAVA

메모리 : 15680 kb

실행 시간 : 116 ms

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

public class Main {
    static int N, M;
    static char[][] map;

    static int[] dr = { -1, 1, 0, 0 };
    static int[] dc = { 0, 0, -1, 1 };

    static boolean[][][][] visit;

    static class Ball {
        int[] Red;
        int[] Blue;
        int move;

        public Ball() {
            this.move = 0;
        }

        public Ball(Ball now) {
            this.Red = now.Red.clone();
            this.Blue = now.Blue.clone();
            this.move = now.move;
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // N, M (3 ≤ N, M ≤ 10)
        N = sc.nextInt();
        M = sc.nextInt();

        // '.'은 빈 칸
        // '#'은 벽
        // 'O'는 구멍의 위치.
        // 'R'은 빨간 구슬의 위치
        // 'B'는 파란 구슬의 위치

        map = new char[N][M];
        Ball b = new Ball();
        for (int i = 0; i < N; i++) {
            String str = sc.next();
            for (int j = 0; j < M; j++) {
                map[i][j] = str.charAt(j);
                if (map[i][j] == 'R') {
                    b.Red = new int[] { i, j };
                    map[i][j] = '.';
                } else if (map[i][j] == 'B') {
                    b.Blue = new int[] { i, j };
                    map[i][j] = '.';
                }
            }
        }

        visit = new boolean[N][M][N][M];
        Queue<Ball> q = new LinkedList<>();
        q.offer(b);
        visit[b.Red[0]][b.Red[1]][b.Blue[0]][b.Blue[1]] = true;
        while (!q.isEmpty()) {
            Ball now = q.poll();
            // 실패
            if (map[now.Blue[0]][now.Blue[1]] == 'O') {
                // System.out.println("공빠져 실패");
                continue;
            } else if (now.move == 11) {
                // System.out.println("시간 초과 실패");
                continue;
            }

            // 성공!
            if (map[now.Red[0]][now.Red[1]] == 'O') {
                System.out.println(now.move);
                System.exit(0);
            }

            for (int dir = 0; dir < 4; dir++) {
                Ball next = move(dir, now);
                if (visit[next.Red[0]][next.Red[1]][next.Blue[0]][next.Blue[1]])
                    continue;
                visit[next.Red[0]][next.Red[1]][next.Blue[0]][next.Blue[1]] = true;
                q.offer(next);
            }
        }

        System.out.println("-1");
    }

    private static Ball move(int dir, Ball now) {
        Ball next = new Ball(now);
        int nr = 0, nc = 0, flag = 0;
        switch (dir) {
        case 0: // 상
            if (next.Red[0] > next.Blue[0]) // 파란공이 더 위에 있으면
                // 위에 있는 파란 공 먼저 움직임.(0)
                flag = 0;
            else
                flag = 1;

            break;
        case 1: // 하
            if (next.Red[0] > next.Blue[0]) // 빨간공이 더 아래 있으면
                // 아래에 있는 빨간 공 먼저 움직임.(1)
                flag = 1;
            else
                flag = 0;

            break;
        case 2: // 좌
            if (next.Red[1] > next.Blue[1]) // 파란공이 더 왼쪽 있으면
                // 왼쪽에 있는 파란 공 먼저 움직임.(0)
                flag = 0;
            else
                flag = 1;
            break;
        case 3: // 우
            if (next.Red[1] > next.Blue[1]) // 빨간공이 더 오른쪽 있으면
                // 오른쪽에 있는 빨간 공 먼저 움직임.(1)
                flag = 1;
            else
                flag = 0;

            break;
        }// end of switch

        // 파란공 먼저 움직이는 경우
        if (flag == 0) {
            nr = next.Blue[0] + dr[dir];
            nc = next.Blue[1] + dc[dir];
            while (map[nr][nc] == '.') {
                nr += dr[dir];
                nc += dc[dir];
            }
            if (map[nr][nc] == 'O') {
                next.Blue = new int[] { nr, nc };
            } else
                next.Blue = new int[] { nr - dr[dir], nc - dc[dir] };

            nr = next.Red[0] + dr[dir];
            nc = next.Red[1] + dc[dir];
            while (map[nr][nc] == '.') {
                if (nr == next.Blue[0] && nc == next.Blue[1])
                    break;
                nr += dr[dir];
                nc += dc[dir];
            }
            if (map[nr][nc] == 'O') {
                next.Red = new int[] { nr, nc };
            } else
                next.Red = new int[] { nr - dr[dir], nc - dc[dir] };
        }
        // 빨간공 먼저 움직이는 경우
        else if (flag == 1) {
            nr = next.Red[0] + dr[dir];
            nc = next.Red[1] + dc[dir];
            while (map[nr][nc] == '.') {
                nr += dr[dir];
                nc += dc[dir];
            }
            if (map[nr][nc] == 'O') {
                next.Red = new int[] { nr, nc };
            } else
                next.Red = new int[] { nr - dr[dir], nc - dc[dir] };

            nr = next.Blue[0] + dr[dir];
            nc = next.Blue[1] + dc[dir];
            while (map[nr][nc] == '.') {
                if (nr == next.Red[0] && nc == next.Red[1])
                    break;
                nr += dr[dir];
                nc += dc[dir];
            }
            if (map[nr][nc] == 'O') {
                next.Blue = new int[] { nr, nc };
            } else
                next.Blue = new int[] { nr - dr[dir], nc - dc[dir] };
        }

        next.move++;
        return next;
    }

}

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

[BOJ] 2003. 수들의 합 2 - (Java)  (2) 2020.03.15
[BOJ] 12100. 2048 (Easy) - (Java)  (0) 2020.03.15
[BOJ] 1062. 가르침 (Java)  (0) 2020.03.14
[BOJ] 14391. 종이 조각 (Java)  (0) 2020.03.14
[BOJ] 2580. 스도쿠 (Java)  (0) 2020.03.13

[BOJ] 1062. 가르침 (Java)

Problem

제출일 : 2020-03-14

문제 풀이 시간 : 1H 30M

난이도 : ★★★


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

남극에 사는 김지민 선생님은 학생들이 되도록이면 많은 단어를 읽을 수 있도록 하려고 한다. 그러나 지구온난화로 인해 얼음이 녹아서 곧 학교가 무너지기 때문에, 김지민은 K개의 글자를 가르칠 시간 밖에 없다. 김지민이 가르치고 난 후에는, 학생들은 그 K개의 글자로만 이루어진 단어만을 읽을 수 있다. 김지민은 어떤 K개의 글자를 가르쳐야 학생들이 읽을 수 있는 단어의 개수가 최대가 되는지 고민에 빠졌다.

남극언어의 모든 단어는 "anta"로 시작되고, "tica"로 끝난다. 남극언어에 단어는 N개 밖에 없다고 가정한다. 학생들이 읽을 수 있는 단어의 최댓값을 구하는 프로그램을 작성하시오.

Input

첫째 줄에 단어의 개수 N과 K가 주어진다. N은 50보다 작거나 같은 자연수이고, K는 26보다 작거나 같은 자연수 또는 0이다. 둘째 줄부터 N개의 줄에 남극 언어의 단어가 주어진다. 단어는 영어 소문자로만 이루어져 있고, 길이가 8보다 크거나 같고, 15보다 작거나 같다. 모든 단어는 중복되지 않는다.

Output

첫째 줄에 김지민이 K개의 글자를 가르칠 때, 학생들이 읽을 수 있는 단어 개수의 최댓값을 출력한다.

Example

input

3 6
antarctica
antahellotica
antacartica

output

2

Solution & Inpression

비트마스킹 + 완전탐색 문제

입력 받은 단어를 분석해 어떤 알파벳이 사용되는지 확인하고 모든 조합을 고려하여 결과를 출력하였다.

모든 단어는 "anta"로 시작되고, "tica"는 부분의 처리와 boolean배열 대신 int를 이용한 비트마스킹을 사용했다면 더 빠른 결과를 출력할 수 있을 것 같다.

Code

언어 : JAVA

메모리 : 15284 kb

실행 시간 : 2140 ms

import java.util.Scanner;

public class Main {
    static int N, K, ans;
    static boolean[][] word;
    static boolean[] visit;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt(); // N은 50보다 작거나 같은 자연수
        K = sc.nextInt(); // K는 26보다 작거나 같은 자연수 또는 0

        // N개의 줄에 남극 언어의 단어
        word = new boolean[N][26];
        for (int i = 0; i < N; i++) {
            String tmp = sc.next();
            for (int j = 0; j < tmp.length(); j++) {
                word[i][tmp.charAt(j) - 'a'] = true;
            }
        }
        visit = new boolean[26];
        ans = 0;
        solve(0, 0);
        System.out.println(ans);
    }

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

    }

    private static void check() {
        int ret = 0;

        boolean flag = true;
        for (int k = 0; k < N; k++) {
            flag = true;
            for (int i = 0; i < 26; i++) {
                if (word[k][i] && !visit[i]) {
                    flag = false;
                    break;
                }
            }
            if (flag)
                ret++;
        }

        ans = Math.max(ret, ans);
    }
}

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

[BOJ] 12100. 2048 (Easy) - (Java)  (0) 2020.03.15
[BOJ] 13460. 구슬 탈출 2 (Java)  (1) 2020.03.14
[BOJ] 14391. 종이 조각 (Java)  (0) 2020.03.14
[BOJ] 2580. 스도쿠 (Java)  (0) 2020.03.13
[BOJ] 9663. N-Queen (Java)  (1) 2020.03.13

[BOJ] 14391. 종이 조각 (Java)

Problem

제출일 : 2020-03-14

문제 풀이 시간 : 1H 45M

난이도 : ★★★


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

영선이는 숫자가 쓰여 있는 직사각형 종이를 가지고 있다. 종이는 1×1 크기의 정사각형 칸으로 나누어져 있고, 숫자는 각 칸에 하나씩 쓰여 있다. 행은 위에서부터 아래까지 번호가 매겨져 있고, 열은 왼쪽부터 오른쪽까지 번호가 매겨져 있다.

영선이는 직사각형을 겹치지 않는 조각으로 자르려고 한다. 각 조각은 크기가 세로나 가로 크기가 1인 직사각형 모양이다. 길이가 N인 조각은 N자리 수로 나타낼 수 있다. 가로 조각은 왼쪽부터 오른쪽까지 수를 이어 붙인 것이고, 세로 조각은 위에서부터 아래까지 수를 이어붙인 것이다.

아래 그림은 4×4 크기의 종이를 자른 한 가지 방법이다.

img

각 조각의 합은 493 + 7160 + 23 + 58 + 9 + 45 + 91 = 7879 이다.

종이를 적절히 잘라서 조각의 합을 최대로 하는 프로그램을 작성하시오.

Input

첫째 줄에 종이 조각의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 4)

둘째 줄부터 종이 조각이 주어진다. 각 칸에 쓰여 있는 숫자는 0부터 9까지 중 하나이다.

Output

영선이가 얻을 수 있는 점수의 최댓값을 출력한다.

Example

input

2 3
123
312

output

435

Solution & Inpression

조금 어려웠던 문제...

모든 경우의 수를 탐색해 최대값을 찾는 과정으로 문제를 푸려 생각을 했지만 모든 경우의 수를 어떻게 만들어야 할지 생각하기가 조금은 힘들었다.

N과 M이 최대 4로 각각의 칸이 종이의 가로 이거나 세로인 2가지 종류가 있기에 모든 경우의 수는 $2^{16}$이기 때문에 완전탐색이 가능했다.

2차원 배열을 1차원 배열로 만들어 인덱스 조작을 편하게 작업했다. r행 i열은 r*M + i로 표현할수 있다.

Code

언어 : JAVA

메모리 : 17152 kb

실행 시간 : 160 ms

import java.util.Scanner;

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

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

        map = new int[N * M];
        for (int i = 0; i < N; i++) {
            String str = sc.next();
            for (int j = 0; j < M; j++) {
                map[i * M + j] = str.charAt(j) - '0';
            }
        }

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

    private static void solve(int idx) {
        if (idx == N * M) {
            check();
            return;
        }

        // 해당 좌표 선택(가로 선택)
        visit[idx] = true;
        solve(idx + 1);

        // 해당 좌표미선택(세로 선택)
        visit[idx] = false;
        solve(idx + 1);

    }

    private static void check() {
        int sum = 0, tmp = 0;

        // 가로
        for (int i = 0; i < N; i++) {
            tmp = 0;
            for (int j = 0; j < M; j++) {
                if (visit[i * M + j]) {
                    tmp *= 10;
                    tmp += map[i * M + j];
                } else {
                    sum += tmp;
                    tmp = 0;
                }
            }
            sum += tmp;
        }

        // 세로
        for (int j = 0; j < M; j++) {
            tmp = 0;
            for (int i = 0; i < N; i++) {
                if (!visit[i * M + j]) {
                    tmp *= 10;
                    tmp += map[i * M + j];
                } else {
                    sum += tmp;
                    tmp = 0;
                }
            }
            sum += tmp;
        }

        // 최대값 갱신
        ans = Math.max(ans, sum);
    }
}

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

[BOJ] 13460. 구슬 탈출 2 (Java)  (1) 2020.03.14
[BOJ] 1062. 가르침 (Java)  (0) 2020.03.14
[BOJ] 2580. 스도쿠 (Java)  (0) 2020.03.13
[BOJ] 9663. N-Queen (Java)  (1) 2020.03.13
[BOJ] 1248. 맞춰봐 (Java)  (0) 2020.03.11

[BOJ] 2580. 스도쿠 (Java)

Problem

제출일 : 2020-03-13

문제 풀이 시간 : 30M

난이도 : ★★


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

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루어진 정사각형 판 위에서 이뤄지는데, 게임 시작 전 몇 몇 칸에는 1부터 9까지의 숫자 중 하나가 쓰여 있다.

img

나머지 빈 칸을 채우는 방식은 다음과 같다.

  1. 각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
  2. 굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.

위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.

img

또한 위쪽 가운데 위치한 3x3 정사각형의 경우에는 3을 제외한 나머지 숫자들이 이미 쓰여있으므로 가운데 빈 칸에는 3이 들어가야 한다.

img

이와 같이 빈 칸을 차례로 채워 가면 다음과 같은 최종 결과를 얻을 수 있다.

img

게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오.

Input

아홉 줄에 걸쳐 한 줄에 9개씩 게임 시작 전 스도쿠판 각 줄에 쓰여 있는 숫자가 한 칸씩 띄워서 차례로 주어진다. 스도쿠 판의 빈 칸의 경우에는 0이 주어진다. 스도쿠 판을 규칙대로 채울 수 없는 경우의 입력은 주어지지 않는다.

Output

모든 빈 칸이 채워진 스도쿠 판의 최종 모습을 아홉줄에 걸쳐 한 줄에 9개씩 한 칸씩 띄워서 출력한다.

스도쿠 판을 채우는 방법이 여럿인 경우는 그 중 하나만을 출력한다.

Example

input

0 3 5 4 6 9 2 7 8
7 8 2 1 0 5 6 0 9
0 6 0 2 7 8 1 3 5
3 2 1 0 4 6 8 9 7
8 0 4 9 1 3 5 0 6
5 9 6 8 2 0 4 1 3
9 1 7 6 5 2 0 8 0
6 0 3 7 0 1 9 5 2
2 5 8 3 9 4 7 6 0

output

1 3 5 4 6 9 2 7 8
7 8 2 1 3 5 6 4 9
4 6 9 2 7 8 1 3 5
3 2 1 5 4 6 8 9 7
8 7 4 9 1 3 5 2 6
5 9 6 8 2 7 4 1 3
9 1 7 6 5 2 3 8 4
6 4 3 7 8 1 9 5 2
2 5 8 3 9 4 7 6 1

Solution & Inpression

N-Queen과 비슷한 백트래킹 유형의 문제

현재 위치에 값이 세로와 가로 3*3칸에 같은 값이 있다면 그 값은 넣을수 없음

Code

언어 : JAVA

메모리 : 21504 kb

실행 시간 : 524 ms

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

public class Main {
    static int[][] map;
    static LinkedList<int[]> list;

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

        map = new int[9][9];
        list = new LinkedList<>();
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                map[i][j] = sc.nextInt();
                if (map[i][j] == 0) {
                    list.add(new int[] { i, j });
                }
            }
        }
        solve(0);
    }

    private static void solve(int depth) {
        if (depth == list.size()) {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    sb.append(map[i][j]).append(" ");
                }
                sb.append("\n");
            }
            sb.append("\n");

            System.out.println(sb);
            System.exit(0);
        }

        int r = list.get(depth)[0];
        int c = list.get(depth)[1];

        for (int i = 1; i <= 9; i++) {
            if (check(r, c, i)) {
                map[r][c] = i;
                solve(depth + 1);
                map[r][c] = 0;
            }
        }

    }

    private static boolean check(int r, int c, int num) {
        if (map[r][c] != 0)
            return false;

        for (int i = 0; i < 9; i++) {
            if (map[i][c] == num || map[r][i] == num) // 가로 세로
                return false;
        }
        int sr = (r / 3) * 3, sc = (c / 3) * 3;
        for (int i = sr; i < sr + 3; i++) {
            for (int j = sc; j < sc + 3; j++) {
                if (map[i][j] == num)  // 3x3
                    return false;
            }
        }

        return true;
    }

}

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

[BOJ] 1062. 가르침 (Java)  (0) 2020.03.14
[BOJ] 14391. 종이 조각 (Java)  (0) 2020.03.14
[BOJ] 9663. N-Queen (Java)  (1) 2020.03.13
[BOJ] 1248. 맞춰봐 (Java)  (0) 2020.03.11
[BOJ] 1339. 단어 수학 (Java)  (0) 2020.03.10

[BOJ] 9663. N-Queen (Java)

Problem

제출일 : 2020-03-13

문제 풀이 시간 : 2H

난이도 : ★★★★


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

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.

N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

Input

첫째 줄에 N이 주어진다. (1 ≤ N < 15)

Output

첫째 줄에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 출력한다.

Example

input

8

output

92-2 5 -3 1

Solution & Inpression

기본 적인 백트래킹 문제.

아래 애니메이션을 보면 해를 탐색하는 과정을 알 수 있다.

Eight-queens-animation.gif

기본적으로 백트래킹은 map을 돌며 해당 위치에 가로, 세로, 대각선으로 중복되지 않게 말을 놓을 수 있는지 확인하고 된다면 다음 말을 놓고 아니면 이전 말의 위치를 변경하는 방법으로 문제를 해결하면 된다.

처음 시도한 코드는 시간초과가 발생했다. 당연히 시간 초과가 발생할것이다. 시간 복잡도를 따져보면 말을 놓을 수 있는 경우의 수는 $_{N\times N}P_N$가 될 것이고 가지치기는 $O(N^2)$의 시간이 소요될것이다.

검사를 해야 하는 항목이 열을 사용하는지 같은 대각선 내에 있는지를 확인하면 되기에 boolean배열을 이용하여 문제를 해결하였다.

Code 1

언어 : JAVA

결과 : 시간 초과

import java.util.Scanner;

public class Main {
    static int N, ans;

    static boolean[][] map;

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

        N = sc.nextInt();

        map = new boolean[N][N];

        ans = 0;
        solve(0);
        System.out.println(ans);
    }

    public static void solve(int r) {
        if (r == N) {
            ans++;
            return;
        }
        for (int c = 0; c < N; c++) {
            if (check(r, c)) {
                map[r][c] = true;
                solve(r + 1);
                map[r][c] = false;
            }
        }
    }

    public static boolean check(int r, int c) {
        if (map[r][c])
            return false;

        for (int i = 0; i < N; i++) {
            if (map[i][c]) // 행 검사
                return false;
            for (int j = 0; j < N; j++) {
                if (i + j == r + c && map[i][j]) // 대각선 검사(/방향)
                    return false;
                if (i - j == r - c && map[i][j]) // 대각선 검사(\방향)
                    return false;
            }
        }
        return true;
    }
}

Code 2

언어 : JAVA

메모리 : 14912 kb

실행 시간 : 2764 ms

import java.util.Scanner;

public class Main {
    static int N, ans;

    static boolean[] col, slash, backslash;

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

        N = sc.nextInt();

        col = new boolean[N];
        slash = new boolean[2 * N - 1];
        backslash = new boolean[2 * N - 1];

        ans = 0;
        solve(0);
        System.out.println(ans);
    }

    public static void solve(int r) {
        if (r == N) {
            ans++;
            return;
        }
        for (int c = 0; c < N; c++) {
            if (check(r, c)) {
                col[c] = slash[r + c] = backslash[r - c + N - 1] = true;
                solve(r + 1);
                col[c] = slash[r + c] = backslash[r - c + N - 1] = false;
            }
        }
    }
    public static boolean check(int r, int c) {
        if (col[c] || slash[r + c] || backslash[r - c + N - 1])
            return false;
        return true;
    }
}

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

[BOJ] 14391. 종이 조각 (Java)  (0) 2020.03.14
[BOJ] 2580. 스도쿠 (Java)  (0) 2020.03.13
[BOJ] 1248. 맞춰봐 (Java)  (0) 2020.03.11
[BOJ] 1339. 단어 수학 (Java)  (0) 2020.03.10
[BOJ] 6064. 카잉 달력(Java)  (0) 2020.03.10

[BOJ] 1248. 맞춰봐 (Java)

Problem

제출일 : 2020-03-11

문제 풀이 시간 : 1H 45M

난이도 : ★★★★


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

규현이는 멍청하다. 왜냐하면, 1~10까지 수 밖에 모르기 때문이다. 어느 날 규현이 옆을 지나가던 태석이가 규현이를 보고 이렇게 외쳤다. "빵빵!!" 규현이는 "아하!" 하면서 세상에는 빵이란 수도 있구나 했다. 그날 이후로 규현이는 매일 친구들을 볼 때면 "빵빵!!" 거리면서 인사를 했다. 규현이의 친구 중에는 태방이가 있다. 자꾸 규현이가 "빵빵!!" 거릴때 마다 자신을 놀리는 것 처럼 생각했던 태방이는 규현이에게 그건 "빵이 아니고 영이야" 라고 가르쳐 줬다.

이제 규현이는 0~10까지 수를 알고 있다. 어느 날 자신이 알고 있는 숫자를 까먹지 않으려고 종이에 1~10까지 수를 썻다. (0은 잠시 까먹었다) 규현이의 친구 석원이는 밀덕이다. 계급을 엄청나게 좋아해서, 규현이가 써 놓은 숫자에 이등병 마크인 -를 모두 그렸다. 석원이는 규현이에게 이렇게 말했다. "너, 우리 위대하신 미하엘 칼라시니코프께서 뭐라고 했는지 알아? 단순함과 신뢰성, 그리고 저렴한 가격이 최고야!"

규현이는 그 말을 듣고서 아하 세상에는 음수도 있구나 했다.

이제 규현이가 아는 수는 -10부터 10까지 20개가 되었다. 아차, 0을 빼먹었구나, 21개가 되었다.

근처 사파리에 놀러간 규현이는 사파리 가이드 승환이와 함께 관광을 시작했다. "저기, 사자 1마리가 보이죠? 그 옆이 그 사자 부인이에요. 그러니깐, 1 더하기 1은 2죠" 규현이는 덧셈을 익혔다. "저 사자는 아까 그 사자의 자식 2마리 입니다. 그럼 총 사자는 몇 마리이지요?" 이제 규현이는 1+1을 제외한 다른 덧셈도 할 수 있다. 만세!

인도네시아에 놀러간 규현이는 자바 섬에 방문했다. 자바 섬에는 자바 커피를 재배하는 홍태석 농부가 있었다. 홍태석은 "ㅋㅋㅋ 님 음수와 양수와 0의 차이도 모름?" 하면서 음수와 양수와 0을 설명해주었다.

지금까지 배운 것을 종합해서, 한국으로 돌아오는 비행기에서 규현이는 종이에 수를 N개 썼다. (규현이가 아는 가장 큰 수는 10이기 때문에, 수를 10개까지만 쓸 수 있다.) 그 다음에, 가능한 모든 N*(N+1)/2개의 구간의 합을 구했다. 이 것을 해인이는 행렬로 표현했다.

규현이가 쓴 수를 A라고 하면, A[i]는 규현이가 i번째 쓴 수이다. 그리고, S[i][j]는 A[i]부터 A[j]까지 합이 0보다 크면 +, 0이면 0, 0보다 작으면 -이다. 여기서 i는 항상 j보다 작거나 같다. 이렇게 배열을 채우면 배열에는 총 N*(N+1)/2개의 문자가 있다. (+, -, 0 중 하나) 이 S 배열이 주어졌을 때, 규현이가 쓴 N개의 수 A를 구해서 출력하면 된다. 규현이는 -10부터 10까지의 정수밖에 모르기 때문에, A도 -10부터 10까지의 정수로만 이루어져 있어야 한다.

Input

첫째 줄에 수열의 크기 N이 주어진다. N은 10보다 작거나 같은 자연수이다. 둘째 줄에는 N(N+1)/2 길이의 문자열이 주어진다. 처음 N개의 문자는 부호 배열의 첫 번째 줄에 해당하고, 다음 N-1개의 문자는 두 번째 줄에 해당한다. 마찬가지로 마지막 문자는 N번째 줄에 해당하는 문자다.

Output

첫째 줄에 수열의 원소 N개를 빈 칸을 사이에 두고 출력한다. 답이 여러 가지 일 경우에는 아무거나 출력하면 된다.

Example

input

4
-+0++++--+

output

-2 5 -3 1

Solution & Inpression

처음 문제를 잃었을 때 이게 도대체 무슨 말이지? 라는 생각을 했다.

문제에서 S[i][j]는 A[i]부터 A[j]까지 합이 0보다 크면 +, 0이면 0, 0보다 작으면 -이다. 라고 했지만 입력을 보면 1차원 배열로 문제가 주어져 어떻게 입력이 들어오는지 확인을 해야 했다.

테스트 케이스를 기준으로 보면 N = 4로 입력은

- + 0 +
  + + +
    - -
      +

의 순으로 입력을 받았다.

처음에는 숫자를 미리 구해 해당 값이 가능한지 판단을 하는 방식으로 문제를 해결하려 생각했다.

경우의 수가 최대 ${21}C{10} = 1,279,935,820,800$이기 때문에 중간에 판단을 먼저 하여 가지치기 하는 방식으로 문제를 해결하였다.

그리고 새롭게 알게 된 것은 System.exit(0)로 프로그램을 종료 시키는 함수이다.

따로 flag 변수를 사용하여 값을 한번만 출력하게 한 것이 아닌 프로그램을 종료 시키는 방식을 사용하였다.

Code

언어 : JAVA

메모리 : 15644 kb

실행 시간 : 464 ms

import java.util.Scanner;

public class Main {
    static int N;
    static char[][] matrix;
    static int[] data;

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

        matrix = new char[N][N];
        String str = sc.next();
        int idx = 0;
        for (int i = 0; i < N; i++) {
            for (int j = i; j < N; j++) {
                matrix[i][j] = str.charAt(idx++);
            }
        }

        data = new int[N];
        solve(0);
    }

    private static void solve(int depth) {
        if (depth == N) {
            for (int i : data) {
                System.out.print(i + " ");
            }
            System.out.println();
            System.exit(0); // 프로그램 종료
        }
        for (int i = -10; i <= 10; i++) {
            data[depth] = i;
            if (check(depth)) {
                solve(depth + 1);
            }
        }
    }

    private static boolean check(int idx) {
        for (int i = 0; i <= idx; i++) {
            int sum = 0;
            for (int j = i; j <= idx; j++) {
                sum += data[j];
                if (matrix[i][j] != (sum == 0 ? '0' : (sum > 0 ? '+' : '-'))) {
                    return false;
                }
            }
        }
        return true;
    }
}

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

[BOJ] 2580. 스도쿠 (Java)  (0) 2020.03.13
[BOJ] 9663. N-Queen (Java)  (1) 2020.03.13
[BOJ] 1339. 단어 수학 (Java)  (0) 2020.03.10
[BOJ] 6064. 카잉 달력(Java)  (0) 2020.03.10
[BOJ] 3055. 탈출 (Java)  (0) 2020.03.08