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

반응형

버블 정렬 (Bubble Sort)

정렬 순서 (오름차순 정렬 기준)

  1. 인접한 원소 두 개((arr[0], arr[1]), (arr[1], arr[2]) ~ (arr[n-2], arr[n-1]))를 쌍으로 지어 앞에 위치한 원소가 뒤의 원소보다 크다면 swap한다.
  2. 1회전을 끝내게 되면 배열 내의 가장 큰 원소가 가장 마지막에 배치된다. (뒤에서부터 정렬이 완료된다.)
  3. 따라서 1~2과정을 정렬되지 않은 원소가 1개가 될때까지 반복한다. 

 

예시

1, 2 회전 후 결과
3,4 회전 후 결과

장점

  • 구현이 쉽고 이해하기 쉽다.
  • In-place Sorting Algorithm
    • 주어진 배열 내에서 이루어지기 때문에 공간복잡도 O(n)
  • 안정 정렬 (Stable Sorting)
    • 중복 값이 입력 순서와 동일하게 정렬 됨

 

단점

  •  O(n2)의 시간복잡도
  • 정렬을 위해 swap 과정이 많이 일어나게 된다. (계산 시간 상승)
    • 다른 O(n2) 시간 복잡도를 갖는 삽입, 선택 정렬보다 오래 걸리게 된다.

 

시간 복잡도 

  • 원소 비교 횟수
    • n-1, n-2, ... ,2, 1 = n(n-1)/2
  • swap: 최악의 경우 비교 횟수마다 교환 진행
    • 3 * (n(n-1)/2)
  • T(n) =  n(n-1)/2 + (3 * (n(n-1)/2)) O(n2) 

 

Code (C/C++)
#include <stdio.h>
#define LENGTH 5

// bubbleSort
void bubbleSort(int* arr){
    // outer loop: 배열 크기 - 1 만큼 반복 (회전 횟수)
    for(int i = 1; i < LENGTH; i++) {
        // inner loop: 각 회전 횟수만큼 뺀 만큼 비교 (회전마다 맨 뒤 공간부터 정렬된다)
        for(int j = 0; j < LENGTH - i; j++) {
            // 이전 원소가 다음 원소보다 큰 경우 swap
            if(arr[j] > arr[j+1]) {
                // swap
                int tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
}

int main() {
    int arr[LENGTH] = {7,4,2,9,5};
    printf("Before Sorting: ");
    for(int i = 0; i<LENGTH; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    bubbleSort(arr);

    printf("After Sorting: ");
    for(int i = 0; i<LENGTH; i++) {
        printf("%d ", arr[i]);
    }
}

Code (Java)
public class sorting {
    public static void main(String[] args) {
        int[] arr = {7, 4, 2, 9, 5};
        System.out.print("Before Sorting: ");
        printArr(arr);
        bubbleSort(arr);
        System.out.print("After Sorting: ");
        printArr(arr);
    }

    // Print Array
    static void printArr(int[] arr) {
        int l = arr.length;
        for (int i = 0; i < l; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    // Bubble sort
    static void bubbleSort(int[] arr) {
        int l = arr.length;
        // outer loop: 배열 크기 - 1 만큼 반복 (회전 횟수)
        for (int i = 1; i < l; i++) {
            // inner loop: 각 회전 횟수만큼 뺀 만큼 비교 (맨 뒤 공간부터 정렬된다)
            for (int j = 0; j < l - i; j++) {
                // 이전 원소가 다음 원소보다 큰 경우 swap
                if (arr[j] > arr[j + 1]) {
                    // swap
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        }
    }
}
반응형

선택 정렬 (Selection Sort)

정렬 순서 (오름차순 정렬 기준)

  1. 주어진 리스트에서 최소 값을 찾는다.
  2. 최소 값을 유효 범위 내에서 맨 앞의 값과 swap한다.
  3. swap된 위치를 제외 한 나머지 공간이 1개가 될때까지 1 ~ 2를 반복한다.

 

예시

1, 2 회전 후 결과
3, 4 회전 후 결과

장점

  • 구현이 쉽고 이해하기 쉽다.
  • In-place Sorting Algorithm
    • 주어진 배열 내에서 이루어지기 때문에 공간복잡도 O(n)

 

단점

  •  O(n2)의 시간복잡도
  • 불안정 정렬 (Unstable sorting)
    • 중복 값 순서 보장 못함

 

시간 복잡도 

  • 외부 루프: (n-1)번 반복
  • 최솟값 탐색: n-1, n-2, n-3, ... ,2, 1
  • T(n) =  1 + 2 + ... + (n-2) + (n-1) = n(n-1)/2 ∈ O(n2) 

 

Code (C/C++) 
#include <stdio.h>
#define LENGTH 5

// Selection Sort
void selectionSort(int* arr){

    // 맨 마지막 원소는 알아서 정렬이 되기 때문에 l-2까지 반복
    for(int cur = 0; cur < LENGTH -1 ; cur++) { 
       int min = cur;
       
       // 최소값 찾기
       for(int j = cur + 1; j < LENGTH; j++) {
           if(arr[min] > arr[j])
                min = j;
       }
       
       // 최소값이 자기 자신인 경우 swap이 필요없다.
      if(min == cur)
        continue;
            
      int tmp = arr[min];
      arr[min] = arr[cur];
      arr[cur] = tmp;
    }
}

int main() {
    int arr[LENGTH] = {7,4,2,9,5};
    printf("Before Sorting: ");
    for(int i = 0; i<LENGTH; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    selectionSort(arr);

    printf("After Sorting: ");
    for(int i = 0; i<LENGTH; i++) {
        printf("%d ", arr[i]);
    }
}
Code (Java)
public class sorting {
    public static void main(String[] args) {
        int[] arr = {7,4,2,9,5};
        System.out.print("Before Sorting: ");
        printArr(arr);
        selectionSort(arr);
        System.out.print("After Sorting: ");
        printArr(arr);
    }

    // Print Array
    static void printArr(int[] arr) {
        int l = arr.length;
        for(int i = 0; i<l; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
    
    // Selection Sort
    static void selectionSort(int[] arr) {
        int l = arr.length;
        
        // 맨 마지막 원소는 알아서 정렬이 되기 때문에 l-2까지 반복
        for(int cur = 0; cur < l-1; cur++) {   
            int min = cur;
            
            // 최소값 찾기
            for(int j = cur + 1; j < l; j++) {
                if(arr[min] > arr[j])
                    min = j;
            }
            
            // 최소값이 자기 자신인 경우 swap이 필요없다.
            if(min == cur)
                continue;
                
            // swap
            int tmp = arr[min];
            arr[min] = arr[cur];
            arr[cur] = tmp;
        }
    }
}
반응형

정렬 알고리즘(Sorting Algorithm)

 정렬 알고리즘이란 n개로 이루어진 배열이 주어져있을 때, 이를 번호순 혹은 사전 순 같이 일정 순서대로 열거를 해주는 알고리즘을 의미한다. 데이터를 정렬하는 이유는 탐색 및 데이터 편집 시 시간을 절약하기 위해서이다. 예를 들어 정렬이 안되어 있는 데이터를 탐색하려면 데이터를 순차적으로 조사해야 하지만 정렬이 되어있을 경우 이진탐색과 같은 알고리즘을 사용하여 탐색시간을 효율적으로 줄일 수 있기 때문이다.

 

삽입 정렬 (Insertion Sort)

정렬 순서

  1. 비교 대상이 되는 원소를 잡고 앞에 있는 원소들과 비교를 한다. 
  2. 비교대상이 되는 원소가 이전 위치의 원소보다 작다면 swap
  3. 마지막 인덱스 원소까지 1~2를 반복한다.

예시

1, 2회전 후 결과
3, 4회전 후 결과 (최종)

장점

  • 구현이 쉽고 이해하기 쉽다.
  • In-place Sorting Algorithm
    • 주어진 배열 내에서 이루어지기 때문에 공간복잡도 O(n)
  • 대부분 정렬이 되어 있는 경우 시간복잡도가 O(n)에 수렴한다. (Best Time Complexity = O(n))
  • 안정 정렬 (Stable Sorting)
    • 중복 값이 입력 순서와 동일하게 정렬 됨.

 

단점

  •  O(n2)의 시간복잡도

 

시간 복잡도 

  • Best Time Complexity
    • 데이터가 이미 정렬되어 있는 경우 
    • 총 비교 횟수는 n-1번이 되기 때문에 O(n)
  • Worst Time Complexity
    • 데이터가 역순으로 정렬되어 있는 경우
    • 매 회전에서는 앞에 놓인 데이터들이 모두 한 칸씩 뒤로 움직여야 한다.
    • 외부 루프: T(n) = 1 +2 + ... + (n-2) + (n-1) = n(n-1)/2 ∈ O(n2)
    • 내부 교환: O(n)
    • Worst Time Complexity: O(n2) + O(n) ∈ O(n2)

 

Code (C/C++) 
#include <stdio.h>
#define LENGTH 5

// insertion Sort
void insertionSort(int* arr){
    for(int i = 1; i<LENGTH; i++) { // 두번째 원소부터 시작
        int cur = arr[i];
        int prev = i - 1;   // 이전 데이터

        // cur 데이터보다 앞에 있는 원소들 중 cur 데이터보다 큰 값이 있는 경우
        while(prev >= 0 && cur < arr[prev]) {
            arr[prev+1] = arr[prev];    // 우측으로 1칸씩 이동
            prev--;
        }
        arr[prev+1] = cur;     // 정렬 순서에 맞는 위치에 저장
    }
}

int main() {
    int arr[LENGTH] = {7,4,2,9,5};
    printf("Before Sorting: ");
    for(int i = 0; i<LENGTH; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    insertionSort(arr);

    printf("After Sorting: ");
    for(int i = 0; i<LENGTH; i++) {
        printf("%d ", arr[i]);
    }
}

 

Code (Java)
public class sorting {
    public static void main(String[] args) {
        int[] arr = {7,4,2,9,5};
        System.out.print("Before Sorting: ");
        printArr(arr);
        insertionSort(arr);
        System.out.print("After Sorting: ");
        printArr(arr);
    }

    // Print Array
    static void printArr(int[] arr) {
        int l = arr.length;
        for(int i = 0; i<l; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
    
    // Insertion Sort
    static void insertionSort(int[] arr) {
        int l = arr.length;
        for(int i = 1; i < l; i++) {   // 두번째 원소부터 시작
            int cur = arr[i];
            int prev = i - 1;    // 이전 데이터

            // cur 데이터보다 앞에 있는 원소들 중 cur 데이터보다 큰 값이 있는 경우
            while(prev >= 0 && cur < arr[prev]){
                arr[prev+1] = arr[prev];    // 우측으로 1칸씩 이동
                prev--;
            }
            arr[prev+1] = cur;  // 정렬 순서에 맞는 위치에 저장
        }
    }
}

 

반응형

이번 여름방학에 참가한 삼성전자DX 알고리즘 특강에서 수강생들에게 B형 시험을 응시 할 수 있는 자격이 주어져서 시험에 응시할 수 있었다. 총 응시 횟수는 4번 중 2번을 선택할 수 있었으며 8월에는 8/6, 8/20에 응시 할 수 있었다. 6일 시험에는 알고리즘 특강에서 아직 자료구조 강의가 끝나지도 않았고 실전 문제들을 연습해보는 시간이 없어서 6일 시험은 건너뛰고 20일 시험에 응시 예약을 했다.

-> 삼성전자DX 부문 알고리즘 특강 후기

삼성전자 역량 테스트란?

삼성 Certi라고도 부르며 삼성에서 주관하는 코딩 테스트라고 생각하면 됩니다. 상시 SW 역량테스트는 3가지가 있으며 아래 사진과 같이 A형(Advanced), B형(Pro), C형(Expert)으로 분류됩니다.

SWEA 내부 설명

위의 설명에는 B형 시험이 라이브러리 사용 불가라고 하지만 이제는 B형에서도 라이브러리 사용 가능하다고 합니다. 더 자세한 내용은 아래 SWEA 사이트에서 확인 할 수 있습니다.

https://swexpertacademy.com/main/capacityTest/main.do

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

준비 과정

이번 삼성전자DX 알고리즘 특강을 수강하며 SWEA사이트를 처음 접하기도 했고 B형 문제유형을 처음 봐서 우선 비슷한 문제 유형들을 많이 풀어보는 시간을 가졌다. 이번 알고리즘 특강에서 B형 시험대비를 위한 실전 문제들도 많이 제공을 해주시고 첫 4주 동안 자료구조를 배우는 기간에도 심화 문제로 비슷한 유형들 문제가 있어 우선 이 문제들을 완벽하게 풀어보자라고 생각을 하여 주어진 문제들로 학습을 하였다.

 

시험 장소 및 시험 환경

8/20에 치뤄진 B형 시험은 삼성전자 서천 인재개발원에서 오프라인으로 실시되었다.
(입구에서 약간의 거리가 있고 보안 검사를 진행하기 때문에 나중에 시험보시는 분들은 여유롭게 가시는 것을 추천드립니다.)
시험장에 도착을 하면 손소독을 실시한 후 지정된 자리에 앉아 시험에 관한 OT를 진행한다. B형의 경우 4시간동안 1문제를 푸는 방식으로 시험 진행 1시간 이후부터 화장실 이동 및 퇴실이 가능하다라고 안내 받았다. 또한, 각 자리에 배치된 데스크탑의 운영체제는 Windows이며 사용 가능한 코드 작성 툴은 Visual Studio 2019(C/C++), Eclipse(JAVA), Pycharm(Python)이 있다.

삼성전자 서천 인재개발원

최종 합격

시험을 보고 4일정도 후에 이메일로 결과가 나왔으며 최종 합격이라고 안내 받았다. 문제를 풀면서 처음 2시간 동안은 오류도 나고 문제를 이해하는 것에 막혀서 조기 퇴실할 까 생각을 했으나 이왕 온 김에 끝까지 해보자라는 생각으로 4시간 꽉 채워서 풀었던 것이 정말 잘했던 일이라고 생각한다. 30분을 남기고 주어진 TC를 모두 통과하여 최적화 시간이 부족했으나 다행히 아슬아슬하게 합격한 것 같습니다. 😄😄

SWEA 내부 등급 업데이트 (Level B)

 

시험 Tip!

우선 B형을 준비하며 공부를 해볼만한 요소들은 Linked List, Greedy, BFS, DFS, Sorting(Merge, Quick, Heap), Binary Search, Tree, Hash, DP 정도 있는 듯 하며 심화 과정으로는 Trie, KMP 까지 해준다면 문제를 구현하기에는 무리가 없을 것이라고 생각합니다. (제가 느끼기에 B형 문제는 문제 로직 자체를 이해하는 것은 할만하나 어떠한 자료구조 및 알고리즘을 사용하여 최적화를 시킬 것인지가 어렵다고 생각합니다.)
그리고 이를 공부하며 시간 복잡도, 공간 복잡도를 잘 이해하시고 각 상황에 맞게 잘 사용하셔야 실행 시간 및 메모리 조건이 까다로운 B형 시험에서 안전하게 pass 할 수 있다고 생각합니다.
B형 시험은 A형과 다르게 모든 TC를 통과하여도 실제 채점 과정에서 TLE에 걸릴 수 있기 때문에 TC를 전부 다 맞았다면 남은 시간동안은 계속 최적화를 시켜 최대한 실행 시간 및 사용 메모리를 줄여주셔야 합니다. 따라서 4시간 동안 시간 배분을 정말 잘 해주셔야 하며 제가 분배한 방법은 문제 파악 및 구현 시뮬레이션: 1시간, 실제 구현: 2시간, 최적화: 1시간으로 처음에 분배를 하여 문제를 풀었습니다.
또한, 시험 중에 일부 자료구조 및 알고리즘의 경우 SWEA에서 제공하는 reference code를 참고할 수 있으니 잘 생각이 안나신다면 참고 하시는 것도 좋은 방법입니다!
(아래는 swea에서 제공되는 reference code 사이트 입니다.)

https://swexpertacademy.com/main/code/referenceCode/referenceCodeList.do

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

반응형

지원 동기

여름방학 기간에 무엇이든 해보려고 이것저것 찾던 중 과 단톡방에 SDS 알고리즘 특강이 올라온 것을 보게 되었다. 기간도 방학동안만 하여 휴학을 할 필요가 없고 삼성에서 직접 알려주는 것이라 매우 좋은기회라 생각이 되었다. 하지만 교육 대상이 4학년 이상이라 3학년인 나는 지원을 못하게 되어 매우 아쉬워하던 중 삼성전자 DX에서도 비슷하게 공고가 올라온 것을 보게 되었는데 대상이 컴퓨터 공학과 소속이라면 누구든지 지원을 할 수 있어서 바로 지원을 하게 되었다. 

알고리즘 특강 모집 공고문

 

신청 및 교육 선발 테스트

 공고문에 있는 링크를 통하여 교육 신청을 하면 이메일로 사전 문제 풀이를 할 수 있는 링크를 받을 수 있다. 

 2022. 1에 진행된 동계 특강에서는 3문제가 나왔다고 들었는데 이번 하계 특강에서는 약 4일간 2문제를 풀면 된다고 안내를 받았다. 문제 유형의 경우 1) DFS, 2) API 구현 (삼성 역량테스트 b형 유형)이 나왔으며 난이도는 DFS의 경우 어느 정도 구현만 할 수 있다면 풀릴 수준의 난이도였다고 생각한다. 2번 문제 같은 경우에는 당시 처음 접해본 유형이라 많은 시간이 걸렸으며 결국 풀지 못하고 2문제중 1솔로 제출을 하게 되었다.

 테스트에 제공된 문제 수가 적기도 하고 1번 문제가 평이했어서 떨어질까 많은 걱정을 하였으나 다행히 합격 메일을 받았다. (후기를 들어보면 2문제중 1솔이 합격선인 것 같았다.)

 

교육 진행 - 기초 교육

 교육의 경우 코로나 때문에 전면 온라인으로 진행이 되었으며 첫 4주 동안은 삼성전자 B형 문제 풀이에 필요한 기초 자료구조부터 심화 자료구조까지 다루는 시간을 가졌다. 매일 사이트에 자료구조에 대한 설명 글과 동영상이 제공이 되었으며 이를 학습한 후 하루에  3~4문제 정도씩 문제 풀이를 하는 식으로 진행이 되었다. 문제 난이도의 경우 기초문제 + 심화 응용 문제가 제공이 되었으며 이에 대한 솔루션은 제공이 되지 않았으나 교육생들끼리 소통할 수 있는 공간을 제공해주시고 코치님들도 댓글로 많은 도움을 주셔서 잘 문제 풀이를 해 나갈 수 있었다.

 

교육 진행 - 심화 문제풀이

 기초 자료구조 교육이 끝난 후 삼성전자 B형 테스트에 대해 설명을 해주시며 그 다음날부터 교육 종료시까지 실전 문제 풀이가 진행이 된다. 매일 1문제씩 제공이 되며 삼성전자 역량테스트 B형 수준과 비슷하게 출제가 되므로 4시간을 맞춰두고 풀이를 진행을 해보면 많이 도움이 될 것 같다고 생각한다. 문제에 대한 풀이는 격일로 진행이 되었으며 실제 코치진들의 문제를 대하는 방법과 다양한 풀이를 통해 시간 단축을 하는 것을 실시간으로 볼 수 있어 많은 도움이 되었다. 

 

삼성전자 SW역량테스트 B형 (Professional) 실시

 이번 dx알고리즘 특강 과정에서는 삼성전자에서 실시하는 역량테스트 B형에 대해 2번의 응시 기회가 주어졌다. (총 4번 중 2번 선택 가능) 원래는 A형을 취득 후 B형을 응시할 수 있는 것으로 알고 있는데 바로 B형을 볼 수 있어 좋은 기회라고 생각했다. 시험은 삼성전자 서천 연수원에서 오프라인으로 실시가 되었으며 이는 다시 자세하게 후기로 올리겠습니다. 

 

후기 및 추천 대상

 비록 방학동안 짧게 진행된 특강이었으나 정말 얻어가는게 많았으며 특히 PS에 대한 접근법과 자료구조들의 다양한 활용 방법을 배운 것 같다.

 자신이 알고리즘 및 자료구조를 어느정도 파악을 하고 있으며 삼성전자 A형 문제들을 풀 수 있는 수준이라면 매우 추천드리며 아직 자료구조를 아직 다 훑어보지 못하고 알고리즘 문제 풀이에 대해 많이 익숙하지 않으신 분은 교육 내에 자료구조를 알려주는 기간이 있으나 문제풀이와 병행을 하려면 따라가기가 좀 힘들 것으로 생각됩니다... 

 또한, 언어는 C/C++, Java, Python이 가능하다고 나와있으나 교육에서 메인이 되는 언어는 C++이며 (코치님들 설명도 전부 C++로 진행) python의 경우에는 사용할 수 있는 문제가 거의 없었던 것으로 기억하여 가급적 C++이나 Java를 사용하는 것을 추천드립니다.

 이번 특강을 통해 자료구조를 더 체계적으로 정리할 수 있었으며 STL에 의존하지 않고 직접 구현을 통해 문제를 푸는 경험도 해볼 수 있어서 실력 향상에 많은 도움이 된 것 같다. 또한 역량테스트 B형 응시 기회를 통해 B형 취득을 하고 알고리즘 문제 풀이 실력도 많이 향상이 된 것 같아 매우 알차고 개인적으로 도움이 많이 되었던 특강이라고 생각합니다!! 

반응형

'etc' 카테고리의 다른 글

[일상] 삼성전자 S/W 역량테스트 B형 후기  (5) 2022.09.22

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

반응형

+ Recent posts