Problem

https://www.acmicpc.net/problem/14499

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지

www.acmicpc.net

크기가 N×M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 이 지도의 위에 주사위가 하나 놓여져 있으며, 주사위의 전개도는 아래와 같다. 지도의 좌표는 (r, c)로 나타내며, r는 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수이다.

  2
4 1 3
  5
  6

주사위는 지도 위에 윗 면이 1이고, 동쪽을 바라보는 방향이 3인 상태로 놓여져 있으며, 놓여져 있는 곳의 좌표는 (x, y) 이다. 가장 처음에 주사위에는 모든 면에 0이 적혀져 있다.

지도의 각 칸에는 정수가 하나씩 쓰여져 있다. 주사위를 굴렸을 때, 이동한 칸에 쓰여 있는 수가 0이면, 주사위의 바닥면에 쓰여 있는 수가 칸에 복사된다. 0이 아닌 경우에는 칸에 쓰여 있는 수가 주사위의 바닥면으로 복사되며, 칸에 쓰여 있는 수는 0이 된다.

주사위를 놓은 곳의 좌표와 이동시키는 명령이 주어졌을 때, 주사위가 이동했을 때 마다 상단에 쓰여 있는 값을 구하는 프로그램을 작성하시오.

주사위는 지도의 바깥으로 이동시킬 수 없다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.

Input

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x, y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다.

둘째 줄부터 N개의 줄에 지도에 쓰여 있는 수가 북쪽부터 남쪽으로, 각 줄은 서쪽부터 동쪽 순서대로 주어진다. 주사위를 놓은 칸에 쓰여 있는 수는 항상 0이다. 지도의 각 칸에 쓰여 있는 수는 10 미만의 자연수 또는 0이다.

마지막 줄에는 이동하는 명령이 순서대로 주어진다. 동쪽은 1, 서쪽은 2, 북쪽은 3, 남쪽은 4로 주어진다.

 

Output

이동할 때마다 주사위의 윗 면에 쓰여 있는 수를 출력한다. 만약 바깥으로 이동시키려고 하는 경우에는 해당 명령을 무시해야 하며, 출력도 하면 안 된다.


문제 해결 과정

 기존 문제들에서 주사위를 구현하는 문제를 자주 접해보지 못하여 주사위가 굴러가는 규칙을 찾아내는 것이 가장 까다로운 문제였다. 

주사위 방향 처리 

 방향 배열 생성

 // 동 서 남 북
    static int[] dx = {0, 0, -1, 1};
    static int[] dy = {1, -1, 0, 0};

 

방향 배열에 맞게 인덱스 조정

 for(int i = 0; i < k; i++) {
        int order = Integer.parseInt(st.nextToken());
        Simulation(order - 1);	// -1을 하여 배열 인덱스에 맞게 조정
 }

위와 같이 배열로 처리 해주었으며 문제에서는 1 ~ 4가 방향이었으므로 배열 인덱스에 맞게 -1을 하여 방향을 받아주었다.

 

 

주사위 규칙 (동, 서, 남, 북 이동에 따른 위치 변화)

 

 위 그림은 주사위가 주어졌을 때 해당 방향으로 구른 후 원래 위치에 따른 이동을 나타낸 것이다. 따라서 이를 코드로 표현 해보면 다음과 같다. (그림과 달리 코드에서는 주사위 인덱스: 0 ~ 5라고 가정한다.) 

        int tmp = dice[5];
        switch (dir) {
            // 동
            case 0:
                dice[5] = dice[2];
                dice[2] = dice[0];
                dice[0] = dice[3];
                dice[3] = tmp;
                break;
            // 서
            case 1:
                dice[5] = dice[3];
                dice[3] = dice[0];
                dice[0] = dice[2];
                dice[2] = tmp;
                break;
            // 남
            case 2:
                dice[5] = dice[4];
                dice[4] = dice[0];
                dice[0] = dice[1];
                dice[1] = tmp;
                break;

            // 북
            default:
                dice[5] = dice[1];
                dice[1] = dice[0];
                dice[0] = dice[4];
                dice[4] = tmp;
        }

 


My Solution Code

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

public class Main {
    static int N, M, X, Y;
    static int[][] map;
    static int[] dice;

    // 동 서 남 북
    static int[] dx = {0, 0, -1, 1};
    static int[] dy = {1, -1, 0, 0};

    static StringBuilder sb;

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

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        X = Integer.parseInt(st.nextToken());
        Y = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());

        map = new int[N][M];
        dice = new int[6];  // 처음 주사위의 모든 면은 0

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

        st = new StringTokenizer(br.readLine());
        for(int i = 0; i < k; i++) {
            int order = Integer.parseInt(st.nextToken());
            Simulation(order - 1);
        }

        System.out.println(sb);
    }

    static void Simulation(int dir) {
        int nx = X + dx[dir];
        int ny = Y + dy[dir];

        // 범위를 벗어난 경우 스킵
        if(nx < 0 || ny < 0 || nx >= N || ny >= M)
            return;

        int tmp = dice[5];
        switch (dir) {
            // 동
            case 0:
                dice[5] = dice[2];
                dice[2] = dice[0];
                dice[0] = dice[3];
                dice[3] = tmp;
                break;
            // 서
            case 1:
                dice[5] = dice[3];
                dice[3] = dice[0];
                dice[0] = dice[2];
                dice[2] = tmp;
                break;
            // 남
            case 2:
                dice[5] = dice[4];
                dice[4] = dice[0];
                dice[0] = dice[1];
                dice[1] = tmp;
                break;

            // 북
            default:
                dice[5] = dice[1];
                dice[1] = dice[0];
                dice[0] = dice[4];
                dice[4] = tmp;
        }

        sb.append(dice[0]).append("\n");

        // 좌표 업데이트
        X = nx;
        Y = ny;

        // 0인경우 주사위 바닥 -> 맵
        if(map[X][Y] == 0) {
            map[X][Y] = dice[5];
        }

        // map이 0이 아닌 경우 맵 -> 주사위 복사, 맵은 0으로 된다.
        else {
            dice[5] = map[X][Y];
            map[X][Y] = 0;
        }
    }
}

반응형

Problem

https://www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

N×M크기의 배열로 표현되는 미로가 있다.

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

미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.

위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.

Input

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

 

Output

첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.


문제 해결 과정

  1. 좌측 상단에서 우측 하단까지의 거리 중 최단거리를 구하는 것이므로 BFS 탐색 알고리즘을 먼저 떠올렸다.
     - BFS 알고리즘의 경우 한 부모 노드에 연결된 자식 노드들을 큐에 넣어서 탐색을 진행하기 때문에 최단 거리를 구하는 문제에 최적화 되어 있다. 
  2. 또한 각 좌표의 정보와 좌측 상단 (0,0)부터 해당 좌표까지의 최단거리를 저장 할 Pair클래스를 생성한다.
  3. (0, 0) 좌표부터 bfs 탐색 시작 
    - 이 때 시작 지점도 칸 수에 포함이 되므로 거리는 1부터 시작!
  4. 탐색하는 좌표가 우측 하단 값 (N-1, M-1)에 도달했다면 값 출력 후 프로그램 종료

 


My Solution Code

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

public class Main {
    static class Pair {
        int x, y, dist;

        public Pair(int x, int y, int dist) {
            this.x = x;
            this.y = y;
            this.dist = dist;
        }
    }

    static int N, M;
    static int[][] maze;
    static boolean[][] visited;

    // 사방 탐색
    static int[] dx = {0, 0, 1, -1};
    static int[] dy = {-1, 1, 0, 0};

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        N = Integer.parseInt(st.nextToken());
        M= Integer.parseInt(st.nextToken());

        maze = new int[N][M];
        visited = new boolean[N][M];

        // 미로 정보 입력
        for(int i = 0; i<N; i++) {
            String str = br.readLine();
            for(int j = 0; j <M; j++) {
                maze[i][j] = str.charAt(j);
            }
        }

        bfs(new Pair(0, 0, 1));
    }

    // 거리 탐색 bfs
    static void bfs(Pair p) {
        Pair[] pairs = new Pair[N*M];
        int front =-1;
        int rear = -1;

        pairs[++rear] = p;

        visited[p.x][p.y] = true;

        while(front != rear) {
            Pair tmp = pairs[++front];

            // (N, M)에 도착하면 종료
            if(tmp.x== N-1 && tmp.y == M-1) {
                System.out.println(tmp.dist);
                break;
            }

            // 사방 탐색 시작
            for(int k = 0; k<4; k++) {
                int nx = tmp.x + dx[k];
                int ny  = tmp.y + dy[k];

                // 범위 내에 있고 방문하지 않았으며 길이 있는 경우
                if(checkValidation(nx, ny) && !visited[nx][ny] && maze[nx][ny] == '1')  {
                    visited[nx][ny] = true;
                    pairs[++rear] = new Pair(nx, ny, tmp.dist+1);
                }
            }
        }

    }

    // 범위 체크
    static boolean checkValidation(int x, int y) {
        return x >= 0 && y >= 0 && x < N && y < M;
    }
}

반응형

Problem

https://www.acmicpc.net/problem/2589

 

2589번: 보물섬

보물섬 지도를 발견한 후크 선장은 보물을 찾아나섰다. 보물섬 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 각 칸은 육지(L)나 바다(W)로 표시되어 있다. 이 지도에서

www.acmicpc.net

보물섬 지도를 발견한 후크 선장은 보물을 찾아나섰다. 보물섬 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 각 칸은 육지(L)나 바다(W)로 표시되어 있다. 이 지도에서 이동은 상하좌우로 이웃한 육지로만 가능하며, 한 칸 이동하는데 한 시간이 걸린다. 보물은 서로 간에 최단 거리로 이동하는데 있어 가장 긴 시간이 걸리는 육지 두 곳에 나뉘어 묻혀있다. 육지를 나타내는 두 곳 사이를 최단 거리로 이동하려면 같은 곳을 두 번 이상 지나가거나, 멀리 돌아가서는 안 된다.

예를 들어 위와 같이 지도가 주어졌다면 보물은 아래 표시된 두 곳에 묻혀 있게 되고, 이 둘 사이의 최단 거리로 이동하는 시간은 8시간이 된다.

보물 지도가 주어질 때, 보물이 묻혀 있는 두 곳 간의 최단 거리로 이동하는 시간을 구하는 프로그램을 작성하시오.

Input

첫째 줄에는 보물 지도의 세로의 크기와 가로의 크기가 빈칸을 사이에 두고 주어진다. 이어 L과 W로 표시된 보물 지도가 아래의 예와 같이 주어지며, 각 문자 사이에는 빈 칸이 없다. 보물 지도의 가로, 세로의 크기는 각각 50이하이다.

 

Output

첫째 줄에 보물이 묻혀 있는 두 곳 사이를 최단 거리로 이동하는 시간을 출력한다.


문제 해결 과정

 문제가 최단 거리 이동 길이 중 가장 큰 값을 구하는 생소한 문제였다. 유형 자체는 처음이라 낯설었지만 실제 문제 접근을 해보면 모든 지점에서 BFS를 돌려 모든 최단 거리 중 가장 긴 구간을 리턴하는 되는 평이한 문제였다. 따라서 주어진 육지에서 모두 BFS를 돌려 max 값을 갱신하면 되는 문제이다. 

 


My Solution Code

// 보물섬
// 거리 -> BFS 생각
// 모든 육지에서 BFS를 돌려 max값 리턴

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

public class Main {

    // 좌표 클래스 (좌표, 거리)
    static class Pair{
        int x, y, len;

        public Pair(int x, int y, int len) {
            this.x = x;
            this.y = y;
            this.len = len;
        }
    }
    
    static char[][] map;
    static boolean[][] visited;
    static int N, M, answer;

    // 사방탐색
    static int[] dx = {0, 0, 1, -1};
    static int[] dy = {1, -1, 0, 0};

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

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

        map = new char[N][M];   // 지도 생성

        answer = 0;

        // map 정보 넣기
        for(int i = 0; i<N; i++) {
            String str = br.readLine();
            for(int j = 0; j<M; j++) {
                map[i][j] = str.charAt(j);
            }
        }

        // 탐색 시작
        for(int i = 0; i<N; i++) {
            for(int j = 0; j<M; j++) {
                // 육지인경우
                if(map[i][j] == 'L') {
                    visited = new boolean[N][M];    // 방문 정보 확인
                    bfs(new Pair(i, j, 0));
                }
            }
        }
        System.out.println(answer);
    }

    static void bfs(Pair p) {
        Queue<Pair> q = new LinkedList<>();
        q.add(p);
        visited[p.x][p.y] = true;

        while(!q.isEmpty()) {
            Pair nextP = q.poll();
            for(int k = 0; k < 4; k++) {
                int nx = nextP.x + dx[k];
                int ny = nextP.y + dy[k];

                // 범위 안에 있으며 방문하지않고 육지인 경우
                if(validationCheck(nx, ny) && !visited[nx][ny] && map[nx][ny] == 'L') {
                    visited[nx][ny] = true;
                    q.add(new Pair(nx, ny, nextP.len + 1));
                    answer = Math.max(answer, nextP.len + 1);
                }
            }
        }
    }

    // 범위안에 있는지 체크
    static boolean validationCheck(int x, int y) {
        if(x < 0 || x >= N || y < 0 || y >= M) {
            return false;
        }
        return true;
    }
}

반응형

Problem

https://www.acmicpc.net/problem/1011

 

1011번: Fly me to the Alpha Centauri

우현이는 어린 시절, 지구 외의 다른 행성에서도 인류들이 살아갈 수 있는 미래가 오리라 믿었다. 그리고 그가 지구라는 세상에 발을 내려 놓은 지 23년이 지난 지금, 세계 최연소 ASNA 우주 비행

www.acmicpc.net

우현이는 어린 시절, 지구 외의 다른 행성에서도 인류들이 살아갈 수 있는 미래가 오리라 믿었다. 그리고 그가 지구라는 세상에 발을 내려 놓은 지 23년이 지난 지금, 세계 최연소 ASNA 우주 비행사가 되어 새로운 세계에 발을 내려 놓는 영광의 순간을 기다리고 있다.

그가 탑승하게 될 우주선은 Alpha Centauri라는 새로운 인류의 보금자리를 개척하기 위한 대규모 생활 유지 시스템을 탑재하고 있기 때문에, 그 크기와 질량이 엄청난 이유로 최신기술력을 총 동원하여 개발한 공간이동 장치를 탑재하였다. 하지만 이 공간이동 장치는 이동 거리를 급격하게 늘릴 경우 기계에 심각한 결함이 발생하는 단점이 있어서, 이전 작동시기에 k광년을 이동하였을 때는 k-1 , k 혹은 k+1 광년만을 다시 이동할 수 있다. 예를 들어, 이 장치를 처음 작동시킬 경우 -1 , 0 , 1 광년을 이론상 이동할 수 있으나 사실상 음수 혹은 0 거리만큼의 이동은 의미가 없으므로 1 광년을 이동할 수 있으며, 그 다음에는 0 , 1 , 2 광년을 이동할 수 있는 것이다. ( 여기서 다시 2광년을 이동한다면 다음 시기엔 1, 2, 3 광년을 이동할 수 있다. )

김우현은 공간이동 장치 작동시의 에너지 소모가 크다는 점을 잘 알고 있기 때문에 x지점에서 y지점을 향해 최소한의 작동 횟수로 이동하려 한다. 하지만 y지점에 도착해서도 공간 이동장치의 안전성을 위하여 y지점에 도착하기 바로 직전의 이동거리는 반드시 1광년으로 하려 한다.

김우현을 위해 x지점부터 정확히 y지점으로 이동하는데 필요한 공간 이동 장치 작동 횟수의 최솟값을 구하는 프로그램을 작성하라.

Input

입력의 첫 줄에는 테스트케이스의 개수 T가 주어진다. 각각의 테스트 케이스에 대해 현재 위치 x 와 목표 위치 y 가 정수로 주어지며, x는 항상 y보다 작은 값을 갖는다. (0 ≤ x < y < 231)

Output

각 테스트 케이스에 대해 x지점으로부터 y지점까지 정확히 도달하는데 필요한 최소한의 공간이동 장치 작동 횟수를 출력한다.


문제 해결 과정

 -> 문제의 중요 포인트

  1. k광년을 이동하였을 때는 k-1 , k 혹은 k+1 광년만을 다시 이동

  2. 도착 직전 이동 거리는 반드시 1광년

     

 중요 포인트의 1, 2에 의해 최소 이동 거리를 구하려면 이동할 수 있는 거리는 점차 증가하다가 중간에서 다시 감소하는 형태가 될 것이다.

이에 대한 규칙을 찾기 위해 몇개의 예시를 나열해보았다.

 

distance  이동 방법 횟 수 제곱 수
1 1 1 O
2 11 2  
3 111 3  
4 121 3 O
5 1211 4  
6 1221 4  
7 12211 5  
8 12221 5  
9 12321 5 O
10 123211 6  
11 123221 6  
12 123321 6  
13 1233211 7  
14 1233221 7  
15 1233321 7  
16 1234321 7 O
17 12343211 8  

 

 이를 보면 distance가 어떠한 수의 제곱이 되는 수인 경우(예시: distance = 4 -> 이동 3번) 다음 distance(예시: distance = 5 -> 이동 4번)에서 최소 이동 횟수가 바뀌며 제곱 수가 되는 distance 사이에서는 중간을 기점으로 최소 이동 횟수가 증가하게 된다.

 

이때 n을 distance의 소수점을 제거한 양의 제곱근이라고 하면 아래와 같은 규칙을 얻을 수 있으며 이를 이용하여 코드 구현을 하였다.

 

    Case 1) distance == n*n:  최소 이동 횟수 == 2*n -1
    Case 2) n*n < distance <= n*n + n:  최소 이동 횟수 == 2*n
    Case 3) n*n + n < distance < (n+1) * (n+1): 최소 이동 횟수 == 2*n + 1

 


My Solution Code

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

public class Main {

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

        int testCase = Integer.parseInt(st.nextToken());
        for(int i = 0; i<testCase; i++){
            String path = br.readLine();
            st = new StringTokenizer(path, " ");
            int start = Integer.parseInt(st.nextToken());
            int dest = Integer.parseInt(st.nextToken());
            int distance = dest - start;
            int sqrtDist = (int)Math.sqrt(distance);
            
            // case 1)
            if(distance == sqrtDist * sqrtDist){
                sb.append(2*sqrtDist - 1).append("\n") ;
            } 
            
            // case 2)
            else if (distance <= sqrtDist*sqrtDist + sqrtDist ){
                sb.append(2*sqrtDist).append("\n") ;
            }
            
            // case 3)
            else {
                sb.append(2*sqrtDist + 1).append("\n") ;
            }
        }
        System.out.println(sb);
    }
}

반응형

Problem

https://www.acmicpc.net/problem/1105

 

1105번: 팔

첫째 줄에 L과 R이 주어진다. L은 2,000,000,000보다 작거나 같은 자연수이고, R은 L보다 크거나 같고, 2,000,000,000보다 작거나 같은 자연수이다.

www.acmicpc.net

L과 R이 주어진다. 이때, L보다 크거나 같고, R보다 작거나 같은 자연수 중에 8이 가장 적게 들어있는 수에 들어있는 8의 개수를 구하는 프로그램을 작성하시오.

Input

첫째 줄에 L과 R이 주어진다. L은 2,000,000,000보다 작거나 같은 자연수이고, R은 L보다 크거나 같고, 2,000,000,000보다 작거나 같은 자연수이다.

 

Output

첫째 줄에 L보다 크거나 같고, R보다 작거나 같은 자연수 중에 8이 가장 적게 들어있는 수에 들어있는 8의 개수를 구하는 프로그램을 작성하시오.


문제 해결 과정

특별한 알고리즘이 생각이 나지 않아 일단 예시를 들며 문제를 접근하였다.

  Case 1 -  L, R의 자리 수가 다른 경우

 

    Case 1의 예시로 주어진 L, R을 생각해보자.

   L: 823부터 R: 8812 까지 사이의 수 중 900 1000 등 8의 개수가 0인 수가 존재하게 된다.

 

    만약 이 두 수의 사이의 간격이 넓어서 이러한 수가 있다고 생각한다면 다른 예시를 보자.

   L: 8, R: 11 인 경우에도 9, 10, 11 등 8의 개수가 0인 수가 존재하게 된다.

    따라서 L, R의 자리 수가 다른 경우에는 답은 0이 된다. 

 

  Case 2, 3 -  L, R의 자리 수가 같은 경우

 

    Case 2의 예시로 주어진 L, R을 생각해보자.

   L: 2314부터 R: 3241 까지 사이의 수에는 L, R 자신을 포함하여 8의 개수가 0인 수가 존재하므로 이러한 경우도 답이 0이 나온다.

 

    하지만 만약 Case 3의 경우 처럼 L 또는 R에 8이 포함되어 있는 경우 최소 개수가 0이 아닐 수 있다.

   Case 3의 경우 처럼 L: 8808, R: 8880인 경우 앞의 두 자리인 88이 겹치게 되므로 최소 2개의 8을 갖게 된다.

   그러므로 자리 수가 같은 경우 두 수를 가장 큰 자리 수부터 비교를 하며 같은 자리에 있는 수가 달라질 때까지 8의 개수를 조사하면 된다.

   따라서 L, R의 자리 수가 같은 경우에는 가장 큰 자리 수부터 같은 자리에 있는 수가 달라질 때까지 존재하는 8의 개수가 답이 된다. 


My Solution Code

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

public class BOJ1105 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        String L = st.nextToken();
        String R = st.nextToken();

        int answer = 0;

        // 자리 수가 다른 경우 답은 0이다.
        if(L.length() != R.length()){
            System.out.println(answer);
        }
        // 자리 수가 같은 경우 각 맨 앞자리 수 부터 비교하며 8의 개수를 센다.
        else {
            int i = 0;
            while( i < L.length() && L.charAt(i) == R.charAt(i)) {
               if(L.charAt(i)  == '8') {
                   answer++;
               }
               i++;
            }
            System.out.println(answer);
        }
    }
}

반응형

Problem

https://www.acmicpc.net/problem/10090

 

10090번: Counting Inversions

A permutation of integers from 1 to n is a sequence a1, a2, ..., an, such that each integer from 1 to n is appeared in the sequence exactly once. Two integers in а permutation form an inversion, when the bigger one is before the smaller one. As an example

www.acmicpc.net

A permutation of integers from 1 to n is a sequence a1, a2, ..., an, such that each integer from 1 to n is appeared in the sequence exactly once.

Two integers in а permutation form an inversion, when the bigger one is before the smaller one.

As an example, in the permutation 4 2 7 1 5 6 3, there are 10 inversions in total. They are the following pairs: 4–2, 4–1, 4–3, 2–1, 7–1, 7–5, 7–6, 7–3, 5–3, 6–3.

Write program invcnt that computes the number of the inversions in a given permutation.

Input

The value for the number n is written on the first line of the standard input. The permutation is written on the second line: n numbers, delimited by spaces.

 

Output

Write the count of inversions on the standard output.


문제 해결 과정

Inversion Counting이라고도 알려진 나름 유명한 문제이다.

 

What is Inversion? 

  임의의 길이가 N인 정수 배열 A가 주어졌을 때, A의 index의 쌍 (i, j)에 대하여 i < j, A[i] > A[j]이면 (i, j)는 A의 Inversion이라고 한다.

 

접근 1 - Brute Force Algorithm

 

   2개의 for loop을 구성하여 outer loop 값보다 큰 inner loop의 값이 존재하는 경우를 counting한다. 

  이때 시간복잡도는 O(n2)가 된다. -> TLE 발생 ( 2≤ n ≤ 1000000)

   따라서 O(n2)보다 효율적인 복잡도를 갖는 알고리즘이 필요하다!!

 

접근 2 - Divide and Conquer

 

 어느 한 배열에서 inversion의 경우를 생각해보면 아래의 그림과 같이 정렬을 하는 과정에서 생기는 교차 점의 개수로 표현이 가능하다.

정렬 과정에서 서로 교차하는 경우의 수

따라서 Merge Sort 과정 중 merge 부분에서 arr[i] > arr[j] 인 경우를 count해주면  O(NlogN)의 시간 복잡도로 구할 수 있다. 

Inversion Counting in Merge Sort


My Solution Code

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

public class BOJ10090 {
    static int[] tmp;
    static int[] arr;
    static long inversion;

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        int N  = Integer.parseInt(br.readLine());   // 순열 길이
        arr = new int[N];
        tmp = new int[N];

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

        inversion = 0;

        mergeSort(0, N-1);

        System.out.println(inversion);
    }

    // divide and conquer
    static void mergeSort(int start, int end) {
        if (start >= end)   // 끝까지 분할 한 경우
            return;

        int mid = (start + end) >> 1; // 중앙 값
        mergeSort(start, mid);  // 좌측 (divide)
        mergeSort(mid+1, end);  // 우측 (divide)
        merge(start, end);  // 병합 (conquer)
    }

    static void merge(int left, int right) {
        int mid = (left + right) >> 1;
        int leftIdx = left;     // 좌측 배열 탐색 인덱스
        int rightIdx = mid+1;   // 우측 배열 탐색 인덱스
        int tempIdx = left;     // 임시 저장 배열 인덱스

        while (leftIdx <= mid && rightIdx <= right) {
            if(arr[leftIdx] <= arr[rightIdx]) {
                tmp[tempIdx++] = arr[leftIdx++];
            } else {
                // inversion 발생 경우 (leftIdx < rightIdx && arr[leftIdx] > arr[rightIdx] )
                tmp[tempIdx++] = arr[rightIdx++];
                inversion += (mid - leftIdx + 1);   // 교차점 count
            }
        }

        // 좌측 잔여 원소 배치
        while(leftIdx <= mid) {
            tmp[tempIdx++] = arr[leftIdx++];
        }

        // 우측 잔여 원소 배치
        while(rightIdx <= right) {
            tmp[tempIdx++] = arr[rightIdx++];
        }

        // copy array
        for(int i = left; i<=right; i++) {
            arr[i] = tmp[i];
        }
    }
}

반응형

Problem

 

https://www.acmicpc.net/problem/1010

 

1010번: 다리 놓기

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.

www.acmicpc.net

재원이는 한 도시의 시장이 되었다. 이 도시에는 도시를 동쪽과 서쪽으로 나누는 큰 일직선 모양의 강이 흐르고 있다. 하지만 재원이는 다리가 없어서 시민들이 강을 건너는데 큰 불편을 겪고 있음을 알고 다리를 짓기로 결심하였다. 강 주변에서 다리를 짓기에 적합한 곳을 사이트라고 한다. 재원이는 강 주변을 면밀히 조사해 본 결과 강의 서쪽에는 N개의 사이트가 있고 동쪽에는 M개의 사이트가 있다는 것을 알았다. (N ≤ M)

재원이는 서쪽의 사이트와 동쪽의 사이트를 다리로 연결하려고 한다. (이때 한 사이트에는 최대 한 개의 다리만 연결될 수 있다.) 재원이는 다리를 최대한 많이 지으려고 하기 때문에 서쪽의 사이트 개수만큼 (N개) 다리를 지으려고 한다. 다리끼리는 서로 겹쳐질 수 없다고 할 때 다리를 지을 수 있는 경우의 수를 구하는 프로그램을 작성하라.

 

Input

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.

Output

각 테스트 케이스에 대해 주어진 조건하에 다리를 지을 수 있는 경우의 수를 출력한다.

 


문제 해결 과정

   -> 문제의 중요 포인트

          1. 한 사이트에는 최대 한 개의 다리만 연결 가능하다.

          2. 서로 다른 다리끼리 겹칠 수 없다.

 

 따라서 문제의 목적은 M개의 동쪽 사이트 중 N개를 위에서부터 1:1로 연결되는 개수를 구하는 것이다. 그러므로 중복없이 선택하는 경우의 수 즉, 조합 (Combination)을 사용하면 된다.

 

 이때, 사용될 조합 공식은 아래와 같다. 

조합 공식

 여기서 아래의 2가지 조합 공식 성질을 이용한다면 DP(Dynamic Programming)를 통하여 문제를 풀 수 있게 된다. 

조합 성질 - 1
조합 성질 - 2

 


My Solution Code 

 

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

public class BackJoon1010 {
    static int[][] dp;  // dp 저장 배열

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;
        int T = Integer.parseInt(br.readLine());    // 테스트 케이스 수

        for(int test_case = 1; test_case <= T; test_case++) {
            st = new StringTokenizer(br.readLine() ," ");
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());

            dp = new int[M+1][N+1];
            sb.append(combination(M, N) + "\n");
        }
        System.out.println(sb);
    }

    static int combination(int n, int r) {
        // 이미 계산된 결과라면 해당 값 반환
        if(dp[n][r] > 0) {
            return dp[n][r];
        }

        // 조합 성질 - 2 사용
        if (r == 0 || n == r) {
            return dp[n][r] = 1;
        }

        // 조합 성질 - 1 사용
        return dp[n][r] = combination(n-1, r-1) + combination(n-1, r);
    }
}

채점 결과

 

 

 

 

반응형

+ Recent posts