선택정렬(Selection Sort)

개념

Selection Sort는 해당 순서에 원소를 넣을 위치는 이미 정해져 있고, 어떤 원소를 넣을지 선택하는 알고리즘입니다.

로직

최소값을 찾아 왼쪽으로 이동시키는데 데이터의 크기만큼 반복하여 정렬하는 방법

  1. 주어진 배열에서 최소값을 찾습니다.
  2. 그 값을 맨앞에 위치한 값과 교채합니다.
  3. 맨처음 위치를 뺀 나머지 배열에 1~2의 과정을 반복합니다.

코드

SelectionSort.java

package Algorithm;

import java.util.Arrays;
import java.util.Random;

public class SelectionSort {

    static final int N = 10;

    public static void main(String[] args) {
        Random random = new Random(); // 랜덤함수를 이용

        int[] arr = new int[N];
        for (int i = 0; i < N; i++) {
            arr[i] = random.nextInt(100); // 0 ~ 99
        }

        System.out.println("정렬 전: " + Arrays.toString(arr));
        selectionSort(arr);
        System.out.println("정렬 후: " + Arrays.toString(arr));
    }

    private static void selectionSort(int[] arr) {
        int index;
        for (int i = 0; i < arr.length; i++) {
            index = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[index] > arr[j]) {
                    index = j;
                }
            }
            swap(arr, index, i);
        }
    }

    private static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

console

정렬 전: [56, 11, 41, 96, 29, 9, 19, 13, 39, 28]
정렬 후: [9, 11, 13, 19, 28, 29, 39, 41, 56, 96]

시간복잡도

주어진 데이터의 개수가 $n$개 일때
$$
\sum_{i=1}^{n-1}n-i=(n-1)+(n-2)+(n-3)+...+1=\frac{n(n-1)}{2}
$$
번의 비교를 하게 된다.

최선, 최악, 평균 모두 $O(N^2)$의 동일한 시간복잡도를 갖습니다.

장점

  1. 알고리즘이 단순하여 구현이 쉽다.
  2. Bubble Sort와 실제로 비교하는 횟수는 같지만 교환하는 횟수가 작기때문에 Bubble Sort에 비해 비교적 효율적입니다.
  3. 추가적인 메모리 공간이 필요하지 않다.

단점

  1. 시간복잡도가 $O(N^2)$으로 비효율적이다.
  2. 불완전 정렬이다.

'Algorithm' 카테고리의 다른 글

병합정렬(Merge sort)  (0) 2020.07.19
퀵정렬(Quick Sort)  (0) 2020.07.19
삽입정렬(Insertion Sort)  (0) 2020.07.19
거품정렬(Bubble Sort)  (0) 2020.07.19
[Algorithm] 최소신장트리(MST)  (0) 2019.11.10

삽입정렬(Insertion Sort)

개념

손 안의 카드를 정렬하는 방법과 유사합니다.

Insertion Sort는 Selection Sort와 유사하지만, 좀 더 효율적인 정렬 알고리즘입니다.

Insertion Sort는 2번째 원소부터 시작하여 그 앞(왼쪽)의 원소들과 비교하여 삽입할 위치를 지정한 후, 원소를 뒤로 옮기고 지정된 자리에 자료를 삽입 하여 정렬하는 알고리즘입니다.

로직

데이터가 정렬되어 있음을 가정하고 값을 해당 위치에 삽입하여 정렬하는 방법

  1. 정렬은 2번째 위치(index)의 값을 temp에 저장합니다.
  2. temp와 이전에 있는 원소들과 비교하며 삽입해나갑니다.
  3. '1'번으로 돌아가 다음 위치(index)의 값을 temp에 저장하고, 반복합니다.

코드

InsertionSort.java

package Algorithm;

import java.util.Arrays;
import java.util.Random;

public class InsertionSort {

    static final int N = 10;

    public static void main(String[] args) {
        Random random = new Random(); // 랜덤함수를 이용

        int[] arr = new int[N];
        for (int i = 0; i < N; i++) {
            arr[i] = random.nextInt(100); // 0 ~ 99
        }

        System.out.println("정렬 전: " + Arrays.toString(arr));
        insertionSort(arr);
        System.out.println("정렬 후: " + Arrays.toString(arr));
    }

    private static void insertionSort(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
            int temp = arr[i];
            int j = i - 1;

            while (j >= 0 && arr[j] > temp) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = temp;
        }
    }
}

console

정렬 전: [81, 14, 9, 34, 0, 88, 41, 89, 71, 26]
정렬 후: [0, 9, 14, 26, 34, 41, 71, 81, 88, 89]

시간복잡도

주어진 데이터의 개수가 $n$개일때
$$
\sum_{i=1}^{n-1}i=1+2+3+...+(n-1)=\frac{n(n-1)}{2}
$$
번의 비교를 하게 된다.

삽입정렬의 시간복잡도는 최악의 경우 $O(N^2)$이며

최선의 경우 $O(N)$이라는 엄청나게 빠른 효율성을 가지고 있어, 다른 정렬 알고리즘의 일부로 사용될 만큼 좋은 정렬 알고리즘입니다.

삽입 정렬의 평균 시간복잡도는 $O(N^2)$입니다.

장점

  1. 알고리즘이 단순하여 구현이 쉽다
  2. 대부분의 원소가 이미 정렬되어 있는 경우 매우 효율적일 수 있다.
  3. 추가적인 메모리 공간을 필요로 하지않는다
  4. 안정 정렬이다.
  5. 다른 $O(N^2)$의 시간복잡도를 갖는 알고리즘에 비해 효율적이다.

단점

  1. 평균과 최악의 시간복잡도가 $O(N^2)$으로 비효율적이다.

'Algorithm' 카테고리의 다른 글

퀵정렬(Quick Sort)  (0) 2020.07.19
선택정렬(Selection Sort)  (0) 2020.07.19
거품정렬(Bubble Sort)  (0) 2020.07.19
[Algorithm] 최소신장트리(MST)  (0) 2019.11.10
[Algorithm] 최소신장트리(MST) - Kruskal  (0) 2019.11.10

거품정렬(Bubble Sort)

개념

Bubble Sort는 서로 인접한 두 원소의 대소를 비교하여 조건에 맞지 않다면 자리를 교환하며 정렬하는 알고리즘입니다.

로직

인접한 데이터 간에 교환이 계속일어나면서 정렬이 이루어지는 방법

  1. 인접한 2개의 데이터를 비교하여 크기가 순서대로 정렬되어 있지 않으면 서로 교환한다
  2. 교환이 이루어지지 않을 때까지 반복한다.

1회전을 수행하고 나면 가장 큰 자료가 맨 뒤로 이동하는 특징이 있다.

코드

BubbleSort.java

아래 작성된 코드는 개선된 버전의 Bubble Sort이다.

1회전을 수행하고 나면 가장 큰 자료가 맨 뒤로 이동하는 특징이 있기 때문에 탐색하는 범위를 줄였으며

버블정렬의 각 단계에서 데이터의 교환이 일어나지 않으면 정렬이 끝난 것을 의미하므로 다음단계를 실행하지 않고 정렬을 종료할 수 있다.

package Algorithm;

import java.util.Arrays;
import java.util.Random;

public class BubbleSort {
    static final int N = 10;

    public static void main(String[] args) {
        Random random = new Random(); // 랜덤함수를 이용

        int[] arr = new int[N];
        for (int i = 0; i < N; i++) {
            arr[i] = random.nextInt(100); // 0 ~ 99
        }

        System.out.println("정렬 전: " + Arrays.toString(arr));
        bubbleSort(arr);
        System.out.println("정렬 후: " + Arrays.toString(arr));
    }

    private static void bubbleSort(int[] arr) {
        boolean flag = true;
        int temp;
        for (int n = arr.length - 1; n > 0 && flag; n--) {
            flag = false;
            for (int i = 0; i < n; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                    flag = true;
                }
            }
        }
    }

    private static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

console

정렬 전: [75, 28, 51, 92, 75, 15, 45, 97, 70, 79]
정렬 후: [15, 28, 45, 51, 70, 75, 75, 79, 92, 97]

시간복잡도

비교 횟수

주어진 데이터의 개수가 $n$개 일때
$$
\sum_{i=1}^{n-1}n-i=(n-1)+(n-2)+(n-3)+...+1=\frac{n(n-1)}{2}
$$
번의 비교를 하게 된다.

최선, 최악, 평균 모두 $O(N^2)$의 동일한 시간복잡도를 갖습니다.

교환 횟수

최악의 경우 원소를 비교할때마다 자리 교환이 일어나므로 최대 ${n(n-1)\over2}$번의 교환이 발생합니다.

장점

  1. 알고리즘이 단순하여 구현이 쉽다.
  2. 추가적인 메모리 공간이 필요하지 않다.
  3. 안정 정렬이다.

단점

  1. 시간복잡도가 $O(N^2)$으로 비효율적이다.
  2. 정렬 되어있지 않은 원소를 정렬하기 위해 교환(swap)이 많이 일어난다.
    • 같은 시간복잡도 Selection Sort는 1번의 교환으로 정렬이 가능하다.

'Algorithm' 카테고리의 다른 글

선택정렬(Selection Sort)  (0) 2020.07.19
삽입정렬(Insertion Sort)  (0) 2020.07.19
[Algorithm] 최소신장트리(MST)  (0) 2019.11.10
[Algorithm] 최소신장트리(MST) - Kruskal  (0) 2019.11.10
[Algorithm] 최소신장트리(MST) - Prim  (0) 2019.11.10

1406. 에디터

제출일 : 2020-06-28

문제 풀이 시간 : 1H

난이도 : Silver III


:link: 문제 link

문제

한 줄로 된 간단한 에디터를 구현하려고 한다. 이 편집기는 영어 소문자만을 기록할 수 있는 편집기로, 최대 600,000글자까지 입력할 수 있다.

이 편집기에는 '커서'라는 것이 있는데, 커서는 문장의 맨 앞(첫 번째 문자의 왼쪽), 문장의 맨 뒤(마지막 문자의 오른쪽), 또는 문장 중간 임의의 곳(모든 연속된 두 문자 사이)에 위치할 수 있다. 즉 길이가 L인 문자열이 현재 편집기에 입력되어 있으면, 커서가 위치할 수 있는 곳은 L+1가지 경우가 있다.

이 편집기가 지원하는 명령어는 다음과 같다.

L 커서를 왼쪽으로 한 칸 옮김 (커서가 문장의 맨 앞이면 무시됨)
D 커서를 오른쪽으로 한 칸 옮김 (커서가 문장의 맨 뒤이면 무시됨)
B 커서 왼쪽에 있는 문자를 삭제함 (커서가 문장의 맨 앞이면 무시됨) 삭제로 인해 커서는 한 칸 왼쪽으로 이동한 것처럼 나타나지만, 실제로 커서의 오른쪽에 있던 문자는 그대로임
P $ $라는 문자를 커서 왼쪽에 추가함

초기에 편집기에 입력되어 있는 문자열이 주어지고, 그 이후 입력한 명령어가 차례로 주어졌을 때, 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 구하는 프로그램을 작성하시오. 단, 명령어가 수행되기 전에 커서는 문장의 맨 뒤에 위치하고 있다고 한다.

입력

첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수를 나타내는 정수 M(1 ≤ M ≤ 500,000)이 주어진다. 셋째 줄부터 M개의 줄에 걸쳐 입력할 명령어가 순서대로 주어진다. 명령어는 위의 네 가지 중 하나의 형태로만 주어진다.

출력

첫째 줄에 모든 명령어를 수행하고 난 후 편집기에 입력되어 있는 문자열을 출력한다.

예제

입력

abcd
3
P x
L
P y

출력

abcdyx

문제 풀이 접근

문제를 처음 풀때 LinkedList를 이용하여 문제를 풀었다. 코드 1을 작성했고 그 결과 시간초과가 발생하였다.

코드 1

언어 : JAVA

결과 : 시간초과

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

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

        String str = sc.next();
        LinkedList<Character> list = new LinkedList<>();

        for (int i = 0; i < str.length(); i++) {
            list.add(str.charAt(i));
        }
        int cursor = str.length();
        int M = sc.nextInt(); // 명령어의 개수

        for (int k = 0; k < M; k++) {
            char cmd = sc.next().charAt(0);
            switch (cmd) {
                case 'L':
                    if (cursor != 0)
                        cursor--;
                    break;
                case 'D':
                    if (cursor != list.size())
                        cursor++;
                    break;
                case 'B':
                    if (cursor != 0)
                        list.remove(--cursor);
                    break;
                case 'P':
                    char c = sc.next().charAt(0);
                    list.add(cursor++, c);
                    break;
            }
        }

        StringBuilder sb = new StringBuilder();
        for (Character c : list) {
            sb.append(c);
        }
        System.out.println(sb);
    }
}

처음 Scanner로 입력을 받고 System.out.println()으로 출력을 했다. 시간초과가 발생해 빠른 BufferedReader과 BufferedWriter로 입출력 방식을 변경했지만 결과는 시간초과가 발생했다.

코드 2

언어 : JAVA

결과 : 시간초과

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String str = br.readLine();

        LinkedList<Character> list = new LinkedList<>();
        for (int i = 0; i < str.length(); i++) {
            list.add(str.charAt(i));
        }
        int cursor = str.length();
        int M = Integer.parseInt(br.readLine()); // 명령어의 개수

        for (int k = 0; k < M; k++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            char cmd = st.nextToken().charAt(0);
            switch (cmd) {
                case 'L':
                    if (cursor != 0)
                        cursor--;
                    break;
                case 'D':
                    if (cursor != list.size())
                        cursor++;
                    break;
                case 'B':
                    if (cursor != 0)
                        list.remove(--cursor);
                    break;
                case 'P':
                    char c = st.nextToken().charAt(0);
                    list.add(cursor++, c);
                    break;
            }
        }

        for (Character c : list) {
            bw.write(c);
        }
        bw.flush();
    }
}

LinkedList는 ArrayList에 비해 임시 배열을 생성하지 않아 삽입/삭제가 더 유리한 장점을 가지고 있다.

하지만 N번째에 값을 삽입하거나 삭제하려면 인덱스를 통한 접근을 할 수 없기에 N번의 탐색과정이 필요하다.

이 시간을 줄여야 했기에 Iterator를 사용하여 문제를 해결했다.

ListIterator는 Iterator를 상속한 인터페이스다.

Iterator의 단방향 탐색과 달리 양방향 탐색이 가능하다.

그렇기에 이 문제처럼 양방향으로 이동하면서 수정하기에 효율적이다.

코드 3

언어 : JAVA

메모리 : 115812 kb

실행 시간 : 668 ms

결과 : 맞았습니다!!

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.StringTokenizer;

public class Silver3_1406_에디터 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String str = br.readLine();

        LinkedList<Character> list = new LinkedList<>();
        for (int i = 0; i < str.length(); i++) {
            list.add(str.charAt(i));
        }
        ListIterator<Character> iter = list.listIterator(list.size());
        int M = Integer.parseInt(br.readLine());

        for (int k = 0; k < M; k++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            switch (st.nextToken()) {
                case "L":
                    if (iter.hasPrevious())
                        iter.previous();
                    break;
                case "D":
                    if (iter.hasNext())
                        iter.next();
                    break;
                case "B":
                    if (iter.hasPrevious()) {
                        iter.previous();
                        iter.remove();
                    }
                    break;
                case "P":
                    iter.add(st.nextToken().charAt(0));
                    break;
            }
        }

        for (Character c : list) {
            bw.write(c);
        }
        bw.flush();
    }
}

문제 해결을 위해 블로그의 글들을 보니 대부분 2개의 스택을 이용하여 구현하여 문제를 해결했다. 스텍을 활용한 문제풀이의 핵심 아이디어는 왼쪽과 오른쪽 스택을 구분지어 커서의 위치를 항상 왼쪽 Top에 위치하여 문제를 푸는 것이다.

코드 4

언어 : JAVA

메모리 : 119188 kb

실행 시간 : 804 ms

결과 : 맞았습니다!!

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Stack;
import java.util.StringTokenizer;

public class Silver3_1406_에디터_ver2 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String str = br.readLine();

        Stack<Character> left = new Stack<>();
        Stack<Character> right = new Stack<>();
        for (int i = 0; i < str.length(); i++) {
            left.add(str.charAt(i));
        }

        int M = Integer.parseInt(br.readLine());

        for (int k = 0; k < M; k++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            switch (st.nextToken()) {
                case "L":
                    if (!left.isEmpty())
                        right.push(left.pop());
                    break;
                case "D":
                    if (!right.isEmpty())
                        left.push(right.pop());
                    break;
                case "B":
                    if (!left.isEmpty())
                        left.pop();
                    break;
                case "P":
                    left.push(st.nextToken().charAt(0));
                    break;
            }
        }

        while (!left.isEmpty()) {
            right.push(left.pop());
        }
        while (!right.isEmpty()) {
            bw.write(right.pop());
        }

        bw.flush();
    }
}

📚 Reference :books:

https://minhamina.tistory.com/17

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

[BOJ] 17822. 원판 돌리기 - (Java)  (0) 2020.06.01
[BOJ] 17780. 새로운 게임 - (Java)  (0) 2020.05.18
[BOJ] 7569. 토마토- (Java)  (0) 2020.05.08
[BOJ] 2644. 촌수계산 - (Java)  (0) 2020.05.07
[BOJ] 3190. 뱀 - (Java)  (0) 2020.05.06

[SWEA] 1208. [S/W 문제해결 기본] 1일차 - Flatten - (Java)

제출일 : 2020-06-19

문제 풀이 시간 : 30M

난이도 : ★★★


link : https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV139KOaABgCFAYh

문제

한 쪽 벽면에 다음과 같이 노란색 상자들이 쌓여 있다.

높은 곳의 상자를 낮은 곳에 옮기는 방식으로 최고점과 최저점의 간격을 줄이는 작업을 평탄화라고 한다.

평탄화를 모두 수행하고 나면, 가장 높은 곳과 가장 낮은 곳의 차이가 최대 1 이내가 된다.

평탄화 작업을 위해서 상자를 옮기는 작업 횟수에 제한이 걸려있을 때, 제한된 횟수만큼 옮기는 작업을 한 후 최고점과 최저점의 차이를 반환하는 프로그램을 작성하시오.

img

가장 높은 곳에 있는 상자를 가장 낮은 곳으로 옮기는 작업을 덤프라고 정의한다.

위의 예시에서 제1회 덤프를 수행한 이후 화면은 다음과 같다.

img

A부분의 상자를 가장 낮은 B부분에 덤프하였으며, A대신 A’부분의 상자를 사용해도 무방하다.

다음은 제2회 덤프를 수행한 이후의 화면이다.

img

A’부분의 상자를 옮겨서, C부분에 덤프하였다. 이때 C 대신 C’부분에 덤프해도 무방하다.

2회의 덤프 후, 최고점과 최저점의 차이는 8 – 2 = 6 이 되었다 (최초덤프 이전에는 9 – 1 = 8 이었다).

덤프 횟수가 2회로 제한된다면, 이 예시 문제의 정답은 6이 된다.

제약 사항

가로 길이는 항상 100으로 주어진다.

모든 위치에서 상자의 높이는 1이상 100이하로 주어진다.

덤프 횟수는 1이상 1000이하로 주어진다.

주어진 덤프 횟수 이내에 평탄화가 완료되면 더 이상 덤프를 수행할 수 없으므로 그 때의 최고점과 최저점의 높이 차를 반환한다 (주어진 data에 따라 0 또는 1이 된다).

입력

총 10개의 테스트 케이스가 주어지며, 각 테스트 케이스의 첫 번째 줄에는 덤프 횟수가 주어진다. 그리고 다음 줄에 각 상자의 높이값이 주어진다.

출력

#부호와 함께 테스트 케이스의 번호를 출력하고, 공백 문자 후 테스트 케이스의 최고점과 최저점의 높이 차를 출력한다.

예제

입력

834
42 68 35 1 70 25 79 59 63 65 6 46 82 28 62 92 96 43 28 37 92 5 3 54 93 83 22 17 19 96 ...
617
16 40 59 5 31 78 7 74 87 22 46 25 73 71 30 78 74 98 13 87 91 62 37 56 68 56 75 32 53 ...
...

출력

#1 13
#2 32
...

문제 풀이 접근

문제를 2가지 방법으로 풀어보았다.

첫번째 방법은 주어진 입력을 그대로 map의 배열에 담은 뒤

  1. 정렬을 수행한다
  2. 맨앞의 값(map[0])은 +1, 맨 뒤의 값(map[99])은 +1의 연산을 수행한다.

위 과정을 dump 수 만큼 반복한 뒤 결과를 출력(map[99]-map[0])하였고

두번째 풀이는 입력을 받을 때 상자의 개수를 카운트 하는 형식으로 값을 담았고 입력을 받을때 min값과 max값을 구하였다.

  1. min개의 개수를 가진 상자를 -1 하고 min+1개의 개수를 가진 상자를 +1
  2. max개의 개수를 가진 상자를 -1 하고 max-1개의 개수를 가진 상자를 +1
  3. map[min], map[max]값이 0이라면 min, max를 조정한다.

위 과정을 dump수만큼 반복한 뒤 결과를 출력(max-min)하였다.

처음 문제에서는 dump 수 만큼 정렬을 진행하기 때문에 속도가 느릴 것 같아 두번째 풀이로 문제를 한번 더 풀어보았다. 생각보다 속도측 면에서 많은 차이가 나지 않은 것이 조금 놀랐다.

코드 1

언어 : JAVA

메모리 : 24,804 kb

실행 시간 : 179 ms

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

public class Solution {
    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream("res/input.txt"));
        Scanner sc = new Scanner(System.in);
        int T = 10;
        int N = 100;
        for (int tc = 1; tc <= T; tc++) {
            int dump = sc.nextInt();
            int[] map = new int[N];
            for (int i = 0; i < N; i++) {
                map[i] = sc.nextInt();
            }
            for (int i = 0; i < dump; i++) {
                Arrays.sort(map);
                map[0]++;
                map[99]--;
            }
            Arrays.sort(map);
            System.out.println("#" + tc + " " + (map[99] - map[0]));
        } // end of TC
    }// end of Main
}

코드 2

언어 : JAVA

메모리 : 24,444 kb

실행 시간 : 160 ms

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

public class Solution {
    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream("res/input.txt"));
        Scanner sc = new Scanner(System.in);
        int T = 10;
        for (int tc = 1; tc <= T; tc++) {
            int dump = sc.nextInt();
            int[] map = new int[101];
            int max = 1;
            int min = 100;
            for (int i = 0; i < 100; i++) {
                int tmp = sc.nextInt();
                map[tmp]++;
                max = Math.max(max, tmp);
                min = Math.min(min, tmp);
            }
            for (int i = 0; i < dump; i++) {
                map[min]--;
                map[min + 1]++;

                map[max]--;
                map[max - 1]++;

                while (map[min] == 0) {
                    min++;
                }

                while (map[max] == 0) {
                    max--;
                }
            }

            System.out.println("#" + tc + " " + (max - min));
        } // end of TC
    }// end of Main
}

[SWEA] 1206. [S/W 문제해결 기본] 1일차 - View - (Java)

제출일 : 2020-06-17

문제 풀이 시간 : 30M

난이도 : ★★★


link : https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV134DPqAA8CFAYh

문제

강변에 빌딩들이 옆으로 빽빽하게 밀집한 지역이 있다.

이곳에서는 빌딩들이 너무 좌우로 밀집하여, 강에 대한 조망은 모든 세대에서 좋지만 왼쪽 또는 오른쪽 창문을 열었을 때 바로 앞에 옆 건물이 보이는 경우가 허다하였다.

그래서 이 지역에서는 왼쪽과 오른쪽으로 창문을 열었을 때, 양쪽 모두 거리 2 이상의 공간이 확보될 때 조망권이 확보된다고 말한다.

빌딩들에 대한 정보가 주어질 때, 조망권이 확보된 세대의 수를 반환하는 프로그램을 작성하시오.

아래와 같이 강변에 8채의 빌딩이 있을 때, 연두색으로 색칠된 여섯 세대에서는 좌우로 2칸 이상의 공백이 존재하므로 조망권이 확보된다. 따라서 답은 6이 된다.

img

A와 B로 표시된 세대의 경우는 왼쪽 조망은 2칸 이상 확보가 되었지만 오른쪽 조망은 한 칸 밖에 확보가 되지 않으므로 조망권을 확보하지 못하였다.

C의 경우는 반대로 오른쪽 조망은 2칸이 확보가 되었지만 왼쪽 조망이 한 칸 밖에 확보되지 않았다.

제약 사항

가로 길이는 항상 1000이하로 주어진다.

맨 왼쪽 두 칸과 맨 오른쪽 두 칸에는 건물이 지어지지 않는다. (예시에서 빨간색 땅 부분)

각 빌딩의 높이는 최대 255이다.

입력

입력 파일의 첫 번째 줄에는 테스트케이스의 길이가 주어진다. 그 바로 다음 줄에 테스트 케이스가 주어진다.

총 10개의 테스트케이스가 주어진다.

출력

#부호와 함께 테스트 케이스의 번호를 출력하고, 공백 문자 후 테스트 케이스의 조망권이 확보된 세대의 수를 출력한다.

예제

입력

100
0 0 225 214 82 73 241 233 179 219 135 62 36 13 6 71 179 77 67 139 31 90 9 37 ...
1000
0 0 225 214 82 73 241 233 179 219 135 62 36 13 6 71 179 77 67 139 31 90 9 37 ...
...

출력

#1 691
#2 9092
...

문제 풀이 접근

현재 위치를 기준으로 좌우 2칸의 빌딩의 높이와 비교하여 문제를 해결하면 된다.

문제 풀이는 좌우 2개의 빌딩의 최대 높이를 구하고 현재 빌딩보다 낮다면 얼마나 낮은지를 카운트 하여 문제를 해결하였습니다.

코드

언어 : JAVA

메모리 : 37,200 kb

실행 시간 : 182 ms

package SWEA.문제해결_기본;

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

public class Day01_View {
    static int N;
    static int[] building;

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

        int T = 10;
        for (int tc = 1; tc <= T; tc++) {
            N = sc.nextInt(); // 가로 길이
            building = new int[N];
            for (int i = 0; i < N; i++) {
                building[i] = sc.nextInt();
            }

            int cnt = 0;
            for (int i = 2; i < N - 2; i++) {
                int max = Math.max(building[i - 2], Math.max(building[i - 1], Math.max(building[i + 1], building[i + 2])));
                if (building[i] - max > 0)
                    cnt += building[i] - max;
            }

            System.out.println("#" + tc + " " + cnt);
        } // end of TC
    } // end of Main
}

[BOJ] 17822. 원판 돌리기 - (Java)

제출일 : 2020-06-01

문제 풀이 시간 : 1H30M

난이도 : ★★★★


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

문제

  • 반지름이 1, 2, ..., N인 원판이 크기가 작아지는 순으로 바닥에 놓여있고, 원판의 중심은 모두 같다. 원판의 반지름이 i이면, 그 원판을 i번째 원판이라고 한다. 각각의 원판에는 M개의 정수가 적혀있고, i번째 원판에 적힌 j번째 수의 위치는 (i, j)로 표현한다. 수의 위치는 다음을 만족한다.

    • (i, 1)은 (i, 2), (i, M)과 인접하다.
    • (i, M)은 (i, M-1), (i, 1)과 인접하다.
    • (i, j)는 (i, j-1), (i, j+1)과 인접하다. (2 ≤ j ≤ M-1)
    • (1, j)는 (2, j)와 인접하다.
    • (N, j)는 (N-1, j)와 인접하다.
    • (i, j)는 (i-1, j), (i+1, j)와 인접하다. (2 ≤ i ≤ N-1)

    아래 그림은 N = 3, M = 4인 경우이다.

    img

    원판의 회전은 독립적으로 이루어진다. 2번 원판을 회전했을 때, 나머지 원판은 회전하지 않는다. 원판을 회전시킬 때는 수의 위치를 기준으로 하며, 회전시킨 후의 수의 위치는 회전시키기 전과 일치해야 한다.

    다음 그림은 원판을 회전시킨 예시이다.

    img img img
    1번 원판을 시계 방향으로 1칸 회전 2, 3번 원판을 반시계 방향으로 3칸 회전 1, 3번 원판을 시계 방향으로 2칸 회전

    원판을 아래와 같은 방법으로 총 T번 회전시키려고 한다. 원판의 회전 방법은 미리 정해져 있고, i번째 회전할때 사용하는 변수는 xi, di, ki이다.

    1. 번호가 xi의 배수인 원판을 di방향으로 ki칸 회전시킨다. di가 0인 경우는 시계 방향, 1인 경우는 반시계 방향이다.
    2. 원판에 수가 남아 있으면, 인접하면서 수가 같은 것을 모두 찾는다.
      1. 그러한 수가 있는 경우에는 원판에서 인접하면서 같은 수를 모두 지운다.
      2. 없는 경우에는 원판에 적힌 수의 평균을 구하고, 평균보다 큰 수에서 1을 빼고, 작은 수에는 1을 더한다.

    원판을 T번 회전시킨 후 원판에 적힌 수의 합을 구해보자.

입력

첫째 줄에 N, M, T이 주어진다.

둘째 줄부터 N개의 줄에 원판에 적힌 수가 주어진다. i번째 줄의 j번째 수는 (i, j)에 적힌 수를 의미한다.

다음 T개의 줄에 xi, di, ki가 주어진다.

출력

원판을 T번 회전시킨 후 원판에 적힌 수의 합을 출력한다.

제한

  • 2 ≤ N, M ≤ 50
  • 1 ≤ T ≤ 50
  • 1 ≤ 원판에 적힌 수 ≤ 1,000
  • 2 ≤ xi ≤ N
  • 0 ≤ di ≤ 1
  • 1 ≤ ki < M

예제

입력

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

출력

22

문제 풀이 접근

시뮬레이션 문제

주어진 조건을 천천히 읽고 놓치는 부분 없이 코딩하면 되는 문제

놓치기 쉬운 부분은

  1. 원판의 수를 지울 때, 원판에 인접한 수가 없는 경우 평균을 구하고 평균보다 큰 수는 1을 빼고 평균보다 작은 수는 1을 더한다.
    • 평균 값을 INT형으로 구하게 된다면 문제의 정답을 구할 수 없다.
  2. 배열을 이용하여 문제를 해결했다. 배열의 0번째 인덱스 값과 M-1번째 인덱스 값은 서로 인접함을 주의 해야 한다.

코드

언어 : JAVA

메모리 : 31940 kb

실행 시간 : 328 ms

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

public class Main {
    static int N, M, T;
    static int[][] map;
    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);
        N = sc.nextInt();
        M = sc.nextInt();
        T = sc.nextInt();

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

        for (int t = 0; t < T; t++) {
            int x = sc.nextInt();
            int d = sc.nextInt();
            int k = sc.nextInt();
            rotate(x, d, k);
            if (!remove())
                replace();
        }

        int ans = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (map[i][j] != -1)
                    ans += map[i][j];
            }
        }
        System.out.println(ans);
    }

    private static void replace() {
        int sum = 0, cnt = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (map[i][j] != -1) {
                    sum += map[i][j];
                    cnt++;
                }
            }
        }
        double avg = sum / (double) cnt;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (map[i][j] != -1) {
                    if ((double) map[i][j] > avg)
                        map[i][j]--;
                    else if ((double) map[i][j] < avg)
                        map[i][j]++;
                }
            }
        }
    }

    private static boolean remove() {
        boolean flag = false;
        boolean[][] visit = new boolean[N][M];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (map[i][j] == -1 || visit[i][j])
                    continue;
                Queue<int[]> q = new LinkedList<>();
                q.add(new int[] { i, j, map[i][j] });
                visit[i][j] = true;
                while (!q.isEmpty()) {
                    int[] now = q.poll();
                    for (int k = 0; k < dr.length; k++) {
                        int nr = now[0] + dr[k];
                        int nc = now[1] + dc[k];

                        if (nr < 0 || nr >= N)
                            continue;

                        if (nc == -1)
                            nc = M - 1;
                        else if (nc == M)
                            nc = 0;
                        if (visit[nr][nc])
                            continue;
                        if (map[nr][nc] == now[2]) {
                            visit[nr][nc] = true;
                            q.add(new int[] { nr, nc, now[2] });
                            map[now[0]][now[1]] = -1;
                            map[nr][nc] = -1;
                            flag = true;
                        }
                    }
                }
            }
        }
        return flag;
    }

    private static void rotate(int x, int d, int k) {
        for (int r = 0; r < N; r++) {
            if ((r + 1) % x != 0)
                continue;
            if (d == 0) {
                // 시계방향 회전
                for (int times = 0; times < k; times++) {
                    // k 칸 이동
                    int[] tmp = map[r].clone();
                    map[r][0] = tmp[M - 1];
                    for (int i = 1; i < M; i++) {
                        map[r][i] = tmp[i - 1];
                    }
                }
            } else {
                // 반시계 방향 회전
                for (int times = 0; times < k; times++) {
                    // k 칸 이동
                    int[] tmp = map[r].clone();
                    map[r][M - 1] = tmp[0];
                    for (int i = 0; i < M - 1; i++) {
                        map[r][i] = tmp[i + 1];
                    }
                }
            }
        }
    }

}

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

1406. 에디터  (0) 2020.06.28
[BOJ] 17780. 새로운 게임 - (Java)  (0) 2020.05.18
[BOJ] 7569. 토마토- (Java)  (0) 2020.05.08
[BOJ] 2644. 촌수계산 - (Java)  (0) 2020.05.07
[BOJ] 3190. 뱀 - (Java)  (0) 2020.05.06

[BOJ] 17780. 새로운 게임 - (Java)

제출일 : 2020-05-18

문제 풀이 시간 : 4H

난이도 : ★★★★


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

문제

재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하나의 말 위에 다른 말을 올릴 수 있다. 체스판의 각 칸은 흰색, 빨간색, 파란색 중 하나로 색칠되어있다.

게임은 체스판 위에 말 K개를 놓고 시작한다. 말은 1번부터 K번까지 번호가 매겨져 있고, 이동 방향도 미리 정해져 있다. 이동 방향은 위, 아래, 왼쪽, 오른쪽 4가지 중 하나이다.

턴 한 번은 1번 말부터 K번 말까지 순서대로 이동시키는 것이다. 한 말이 이동할 때 위에 올려져 있는 말도 함께 이동하며, 가장 아래에 있는 말만 이동할 수 있다. 말의 이동 방향에 있는 칸에 따라서 말의 이동이 다르며 아래와 같다. 턴이 진행되던 중에 말이 4개 이상 쌓이는 순간 게임이 종료된다.

  • A번 말이 이동하려는 칸이
    • 흰색인 경우에는 그 칸으로 이동한다. 이동하려는 칸에 말이 이미 있는 경우에는 가장 위에 A번 말을 올려놓는다.
      • A번 말의 위에 다른 말이 있는 경우에는 A번 말과 위에 있는 모든 말이 이동한다.
      • 예를 들어, A, B, C로 쌓여있고, 이동하려는 칸에 D, E가 있는 경우에는 A번 말이 이동한 후에는 D, E, A, B, C가 된다.
    • 빨간색인 경우에는 이동한 후에 A번 말과 그 위에 있는 모든 말의 쌓여있는 순서를 반대로 바꾼다.
      • A, B, C가 이동하고, 이동하려는 칸에 말이 없는 경우에는 C, B, A가 된다.
      • A, D, F, G가 이동하고, 이동하려는 칸에 말이 E, C, B로 있는 경우에는 E, C, B, G, F, D, A가 된다.
    • 파란색인 경우에는 A번 말의 이동 방향을 반대로 하고 한 칸 이동한다. 방향을 반대로 한 후에 이동하려는 칸이 파란색인 경우에는 이동하지 않고 방향만 반대로 바꾼다.
    • 체스판을 벗어나는 경우에는 파란색과 같은 경우이다.

다음은 크기가 4×4인 체스판 위에 말이 4개 있는 경우이다.

img

첫 번째 턴은 다음과 같이 진행된다.

img img img img

두 번째 턴은 다음과 같이 진행된다.

img img img img

체스판의 크기와 말의 위치, 이동 방향이 모두 주어졌을 때, 게임이 종료되는 턴의 번호를 구해보자.

입력

첫째 줄에 체스판의 크기 N, 말의 개수 K가 주어진다. 둘째 줄부터 N개의 줄에 체스판의 정보가 주어진다. 체스판의 정보는 정수로 이루어져 있고, 각 정수는 칸의 색을 의미한다. 0은 흰색, 1은 빨간색, 2는 파란색이다.

다음 K개의 줄에 말의 정보가 1번 말부터 순서대로 주어진다. 말의 정보는 세 개의 정수로 이루어져 있고, 순서대로 행, 열의 번호, 이동 방향이다. 행과 열의 번호는 1부터 시작하고, 이동 방향은 4보다 작거나 같은 자연수이고 1부터 순서대로 →, ←, ↑, ↓의 의미를 갖는다.

같은 칸에 말이 두 개 이상 있는 경우는 입력으로 주어지지 않는다.

출력

게임이 종료되는 턴의 번호를 출력한다. 그 값이 1,000보다 크거나 절대로 게임이 종료되지 않는 경우에는 -1을 출력한다.

제한

  • 4 ≤ N ≤ 12
  • 4 ≤ K ≤ 10

예제

입력

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

출력

-1

문제 풀이 접근

시뮬레이션 문제

턴 한 번은 1번 말부터 K번 말까지 순서대로 이동시키는 것이다. 한 말이 이동할 때 위에 올려져 있는 말도 함께 이동하며, 가장 아래에 있는 말만 이동할 수 있다.

말을 옮길 때 현재 움직이려는 말이 가장 밑에 있는 말인지 확인하는 과정을 거쳐야 한다. -> state[][]을 사용!

코드

언어 : JAVA

메모리 : 14840 kb

실행 시간 : 136 ms

package BOJ.Simulation;

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

public class Gold2_17780_새로운게임 {
    static final int WHITE = 0, RED = 1, BLUE = 2;
    static final int[] change = { 1, 0, 3, 2 };
    // →, ←, ↑, ↓
    static final int[] dr = { 0, 0, -1, 1 };
    static final int[] dc = { 1, -1, 0, 0 };

    static int N, K;
    static int[][] map;
    static LinkedList<Integer>[][] state;
    static Horse[] horses;

    static class Horse {
        int r, c, dir;

        Horse(int r, int c, int dir) {
            this.r = r;
            this.c = c;
            this.dir = dir;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt(); // 체스판의 크기 N
        K = sc.nextInt(); // 말의 개수 K

        map = new int[N][N]; // 체스판 색상 정보
        state = new LinkedList[N][N]; // 체스판에 쌓인 말의 순서

        // 체스판의 정보
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                map[i][j] = sc.nextInt();
                state[i][j] = new LinkedList<>();
            }
        }

        // 말의 정보
        horses = new Horse[K + 1]; // 1 ~ K번 말
        for (int i = 1; i <= K; i++) {
            int r = sc.nextInt() - 1;
            int c = sc.nextInt() - 1;
            int dir = sc.nextInt() - 1;
            horses[i] = new Horse(r, c, dir);
            state[r][c].add(i);
        }

        System.out.println(start());

    }

    private static int start() {
        boolean flag = true;
        int times = 0;
        while (flag) {
            times++;
            if (times > 1000)
                break;

            for (int i = 1; i <= K; i++) {
                Horse h = horses[i];
                int r = h.r;
                int c = h.c;

                // 가장 아래쪽 말이 아니라면 PASS
                if (state[r][c].get(0) != i)
                    continue;

                int nr = r + dr[h.dir];
                int nc = c + dc[h.dir];

                // 말이 이동하려는 칸이 파란색인 경우 + 체스판을 벗어나는 경우
                if (!isRange(nr, nc) || map[nr][nc] == BLUE) {
                    // 방향 반대로
                    h.dir = change[h.dir];
                    nr = r + dr[h.dir];
                    nc = c + dc[h.dir];
                }

                // 방향을 반대로 한 후에 이동하려는 칸이 파란색인 경우
                if (!isRange(nr, nc) || map[nr][nc] == BLUE) {
                    continue;
                }
                // 말이 이동하려는 칸이 빨간색인 경우
                else if (map[nr][nc] == RED) {
                    // 순서를 반대로 모든 말이 이동
                    for (int j = state[r][c].size() - 1; j >= 0; j--) {
                        int tmp = state[r][c].get(j);
                        state[nr][nc].add(tmp);
                        horses[tmp].r = nr;
                        horses[tmp].c = nc;
                    }
                    state[r][c].clear();
                }
                // 말이 이동하려는 칸이 흰색인 경우
                else {
                    // 모든 말이 이동
                    for (int j = 0; j < state[r][c].size(); j++) {
                        int tmp = state[r][c].get(j);
                        state[nr][nc].add(tmp);
                        horses[tmp].r = nr;
                        horses[tmp].c = nc;
                    }
                    state[r][c].clear();
                }

                // 이동한 곳에 말이 4개 이상 있는가?
                if (state[nr][nc].size() >= 4) {
                    flag = false;
                    break;
                }
            }
        }
        return flag ? -1 : times;
    }

    static boolean isRange(int r, int c) {
        return 0 <= r && r < N && 0 <= c && c < N;
    }
}

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

1406. 에디터  (0) 2020.06.28
[BOJ] 17822. 원판 돌리기 - (Java)  (0) 2020.06.01
[BOJ] 7569. 토마토- (Java)  (0) 2020.05.08
[BOJ] 2644. 촌수계산 - (Java)  (0) 2020.05.07
[BOJ] 3190. 뱀 - (Java)  (0) 2020.05.06