문제

문제 설명

네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있을 때 컴퓨터 A와 컴퓨터 C도 간접적으로 연결되어 정보를 교환할 수 있습니다. 따라서 컴퓨터 A, B, C는 모두 같은 네트워크 상에 있다고 할 수 있습니다.

컴퓨터의 개수 n, 연결에 대한 정보가 담긴 2차원 배열 computers가 매개변수로 주어질 때, 네트워크의 개수를 return 하도록 solution 함수를 작성하시오.

제한사항

  • 컴퓨터의 개수 n은 1 이상 200 이하인 자연수입니다.
  • 각 컴퓨터는 0부터 n-1인 정수로 표현합니다.
  • i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현합니다.
  • computer[i][i]는 항상 1입니다.

입출력 예

n computers return

3 [[1, 1, 0], [1, 1, 0], [0, 0, 1]] 2
3 [[1, 1, 0], [1, 1, 1], [0, 1, 1]] 1

입출력 예 설명

예제 #1

아래와 같이 2개의 네트워크가 있습니다.

예제 #2

아래와 같이 1개의 네트워크가 있습니다.

풀이

DFS 또는 BFS로 풀 수 있는 문제로, 나는 BFS를 사용해서 풀었다. 각 노드들을 순회하면서, 만약 방문하지 않은 노드라면 큐에 넣은 후 연결된 노드를 모두 탐색하여 방문처리 해준다. 방문처리가 끝났다면 answer 값을 증가 시켜주면 된다.

코드

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

class Solution {
    public int solution(int n, int[][] computers) {
        int answer = 0;
        boolean[] visited = new boolean[n];
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                queue.add(i);
                while (!queue.isEmpty()) {
                    int idx = queue.poll();
                    for (int j = 0; j < computers[idx].length; j++) {
                        if (!visited[j] && computers[idx][j] == 1) {
                            visited[j] = true;
                            queue.add(j);
                        }
                    }
                }
                answer++;
            }
        }

        return answer;
    }
}

문제

문제 설명

이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.

명령어 수신 탑(높이)

I 숫자 큐에 주어진 숫자를 삽입합니다.
D 1 큐에서 최댓값을 삭제합니다.
D -1 큐에서 최솟값을 삭제합니다.

이중 우선순위 큐가 할 연산 operations가 매개변수로 주어질 때, 모든 연산을 처리한 후 큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return 하도록 solution 함수를 구현해주세요.

제한사항

  • operations는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.
  • operations의 원소는 큐가 수행할 연산을 나타냅니다.
    • 원소는 “명령어 데이터” 형식으로 주어집니다.- 최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.
  • 빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.

입출력 예

operations return

["I 16", "I -5643", "D -1", "D 1", "D 1", "I 123", "D -1"] [0,0]
["I -45", "I 653", "D 1", "I -642", "I 45", "I 97", "D 1", "D -1", "I 333"] [333, -45]

입출력 예 설명

입출력 예 #1

  • 16과 -5643을 삽입합니다.
  • 최솟값을 삭제합니다. -5643이 삭제되고 16이 남아있습니다.
  • 최댓값을 삭제합니다. 16이 삭제되고 이중 우선순위 큐는 비어있습니다.
  • 우선순위 큐가 비어있으므로 최댓값 삭제 연산이 무시됩니다.
  • 123을 삽입합니다.
  • 최솟값을 삭제합니다. 123이 삭제되고 이중 우선순위 큐는 비어있습니다.

따라서 [0, 0]을 반환합니다.

입출력 예 #2

  • 45와 653을 삽입후 최댓값(653)을 삭제합니다. -45가 남아있습니다.
  • 642, 45, 97을 삽입 후 최댓값(97), 최솟값(-642)을 삭제합니다. -45와 45가 남아있습니다.
  • 333을 삽입합니다.

이중 우선순위 큐에 -45, 45, 333이 남아있으므로, [333, -45]를 반환합니다.

풀이

문제의 제목처럼, 우선순위 큐를 최댓값을 기준으로 꺼내는 큐, 최솟값을 기준으로 꺼내는 큐 두가지를 가지도록 한다. 이 후 I 연산이 나왔을 때 두 큐에 값을 추가해주고, D 연산이 나왔을 때에는 각 큐에서 값 제거 후 다른 큐에 남아있는 동일한 값을 제거해준다. 이후 answer 배열에 최댓값, 최솟값을 담아서 리턴해준다.

코드

import java.util.Comparator;
import java.util.PriorityQueue;

class Solution {
    public int[] solution(String[] operations) {
        int[] answer = new int[2];
        PriorityQueue<Integer> maxQueue = new PriorityQueue<Integer>(Comparator.reverseOrder());
        PriorityQueue<Integer> minQueue = new PriorityQueue<Integer>();
        for (int i = 0; i < operations.length; i++) {
            String[] splited = operations[i].split(" ");
            if (splited[0].equals("I")) {
                maxQueue.add(Integer.parseInt(splited[1]));
                minQueue.add(Integer.parseInt(splited[1]));
            } else {
                if (splited[1].equals("1") && !maxQueue.isEmpty()) {
                    minQueue.remove(maxQueue.poll());
                } else if (splited[1].equals("-1") && !minQueue.isEmpty()) {
                    maxQueue.remove(minQueue.poll());
                }

            }
        }
        if (!maxQueue.isEmpty())
            answer[0] = maxQueue.poll();
        if (!minQueue.isEmpty())
            answer[1] = minQueue.poll();

        return answer;
    }
}

문제

문제 설명

두 수의 최소공배수(Least Common Multiple)란 입력된 두 수의 배수 중 공통이 되는 가장 작은 숫자를 의미합니다. 예를 들어 2와 7의 최소공배수는 14가 됩니다. 정의를 확장해서, n개의 수의 최소공배수는 n 개의 수들의 배수 중 공통이 되는 가장 작은 숫자가 됩니다. n개의 숫자를 담은 배열 arr이 입력되었을 때 이 수들의 최소공배수를 반환하는 함수, solution을 완성해 주세요.

제한 사항

  • arr은 길이 1이상, 15이하인 배열입니다.
  • arr의 원소는 100 이하인 자연수입니다.

입출력 예

arr result

[2,6,8,14] 168
[1,2,3] 6

풀이

n개의 주어진 수들의 최소 공배수를 구하는 문제이다.

a와 b의 최소공배수는 a와 b의 곱을 a와 b의 최대공약수를 나눈 것과 같다. 라는 특성을 활용하면 된다.

따라서, 먼저 유클리드 호제법을 활용하여 최대공약수를 계산하고, 그 결과를 활용하여 최소공배수를 업데이트 시켜주면 된다.

코드

class Solution {
 public static int gcd(int a, int b) {
        int x = Math.max(a,b);
        int y = Math.min(a,b);

        while (x % y != 0) {
            int r = x % y;
            x = y;
            y = r;
        }
        return y;
    }
    public int solution(int[] arr) {
        int answer = arr[0];

        for (int i = 1; i < arr.length; i++) {
            int gcd = gcd(answer, arr[i]);

            answer = answer * arr[i] / gcd;
        }
        return answer;
    }
}

문제

문제 설명

정수로 이루어진 배열 numbers가 있습니다. 배열 의 각 원소들에 대해 자신보다 뒤에 있는 숫자 중에서 자신보다 크면서 가장 가까이 있는 수를 뒷 큰수라고 합니다.

정수 배열 numbers가 매개변수로 주어질 때, 모든 원소에 대한 뒷 큰수들을 차례로 담은 배열을 return 하도록 solution 함수를 완성해주세요. 단, 뒷 큰수가 존재하지 않는 원소는 -1을 담습니다.


제한사항

  • 4 ≤ numbers의 길이 ≤ 1,000,000
    • 1 ≤ numbers[i] ≤ 1,000,000

입출력 예

numbers result

[2, 3, 3, 5] [3, 5, 5, -1]
[9, 1, 5, 3, 6, 2] [-1, 5, 6, 6, -1, -1]

입출력 예 설명

입출력 예 #1

2의 뒷 큰수는 3입니다. 첫 번째 3의 뒷 큰수는 5입니다. 두 번째 3 또한 마찬가지입니다. 5는 뒷 큰수가 없으므로 -1입니다. 위 수들을 차례대로 배열에 담으면 [3, 5, 5, -1]이 됩니다.

입출력 예 #2

9는 뒷 큰수가 없으므로 -1입니다. 1의 뒷 큰수는 5이며, 5와 3의 뒷 큰수는 6입니다. 6과 2는 뒷 큰수가 없으므로 -1입니다. 위 수들을 차례대로 배열에 담으면 [-1, 5, 6, 6, -1, -1]이 됩니다.

풀이

numbers의 길이가 1,000,000 이므로, 완전탐색으로는 풀 수 없는 문제이다.

Stack을 통해 뒤의 인덱스 부터 접근하여 비교할 경우 시간을 줄일 수 있을것이라고 생각하고 구현했다.

코드

import java.util.Stack;

class Solution {
    public int[] solution(int[] numbers) {
        // stack + 뒤에서 접근 한다면 풀릴거같다.
        Stack<Integer> stack = new Stack<>();
        Stack<Integer> result = new Stack<>();

        // 가장 마지막 원소는 항상 -1이다.
        stack.add(numbers[numbers.length-1]);
        result.add(-1);
        for (int i = numbers.length - 2 ; i >= 0; i--) {
            while(!stack.isEmpty()) {
                if (stack.peek() > numbers[i]) {
                    result.add(stack.peek());
                    break;
                } else {
                    stack.pop();
                }
            }
            if (stack.isEmpty())
                result.add(-1);

            stack.add(numbers[i]);
        }
        
        int[] answer = new int[numbers.length];
        for (int i = 0; i < numbers.length; i++) {
            answer[i] = result.pop();
        }
        return answer;
    }
}

문제

문제 설명

비내림차순으로 정렬된 수열이 주어질 때, 다음 조건을 만족하는 부분 수열을 찾으려고 합니다.

  • 기존 수열에서 임의의 두 인덱스의 원소와 그 사이의 원소를 모두 포함하는 부분 수열이어야 합니다.
  • 부분 수열의 합은 k입니다.
  • 합이 k인 부분 수열이 여러 개인 경우 길이가 짧은 수열을 찾습니다.
  • 길이가 짧은 수열이 여러 개인 경우 앞쪽(시작 인덱스가 작은)에 나오는 수열을 찾습니다.

수열을 나타내는 정수 배열 sequence와 부분 수열의 합을 나타내는 정수 k가 매개변수로 주어질 때, 위 조건을 만족하는 부분 수열의 시작 인덱스와 마지막 인덱스를 배열에 담아 return 하는 solution 함수를 완성해주세요. 이때 수열의 인덱스는 0부터 시작합니다.


제한사항

  • 5 ≤ sequence의 길이 ≤ 1,000,000
    • 1 ≤ sequence의 원소 ≤ 1,000
    • sequence는 비내림차순으로 정렬되어 있습니다.
  • 5 ≤ k ≤ 1,000,000,000
    • k는 항상 sequence의 부분 수열로 만들 수 있는 값입니다.

입출력 예

sequence k result

[1, 2, 3, 4, 5] 7 [2, 3]
[1, 1, 1, 2, 3, 4, 5] 5 [6, 6]
[2, 2, 2, 2, 2] 6 [0, 2]

입출력 예 설명

입출력 예 #1

[1, 2, 3, 4, 5]에서 합이 7인 연속된 부분 수열은 [3, 4]뿐이므로 해당 수열의 시작 인덱스인 2와 마지막 인덱스 3을 배열에 담아 [2, 3]을 반환합니다.

입출력 예 #2

[1, 1, 1, 2, 3, 4, 5]에서 합이 5인 연속된 부분 수열은 [1, 1, 1, 2], [2, 3], [5]가 있습니다. 이 중 [5]의 길이가 제일 짧으므로 해당 수열의 시작 인덱스와 마지막 인덱스를 담은 [6, 6]을 반환합니다.

입출력 예 #3

[2, 2, 2, 2, 2]에서 합이 6인 연속된 부분 수열은 [2, 2, 2]로 3가지 경우가 있는데, 길이가 짧은 수열이 여러 개인 경우 앞쪽에 나온 수열을 찾으므로 [0, 2]를 반환합니다.

풀이

기존 풀이

예전에 시도했던 문제여서 코드가 있었다. 이 코드의 경우 이중 for 문을 이용하게 되는데, sequence의 길이가 1,000,000 이하 이므로, 최악의 경우 100만 X 100만번의 연산을 수행하므로 결과 확인 시 시간초과가 발생하는것을 확인했다. 따라서, 연산횟수를 줄이는 방법을 고민해봐야 했다.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

class Solution {
    public int[] solution(int[] sequence, int k) {
        int[] answer;

        ArrayList<int []> arrayList = new ArrayList<int[]>();

        for (int i = 0; i < sequence.length; i++) {
            int sum = 0;
            int start = i;
            int end;
            for (int j = i; j < sequence.length; j++) {
                sum+= sequence[j];
                if (sum == k) {
                    end = j;
                    arrayList.add(new int[] {start, end});
                }else if (sum > k)
                    break;
            }

        }
        arrayList.sort(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return (o1[1] - o1[0]) - (o2[1] - o2[0]);
            }
        });

        //System.out.println(arrayList);
        answer = arrayList.get(0);

        return answer;
    }
}

코드 실행 결과

테스트 1 〉	통과 (2.62ms, 77.1MB)
테스트 2 〉	통과 (2.21ms, 79.4MB)
테스트 3 〉	통과 (2.51ms, 78.2MB)
테스트 4 〉	통과 (12.93ms, 78.4MB)
테스트 5 〉	통과 (25.50ms, 77.9MB)
테스트 6 〉	통과 (437.48ms, 80.8MB)
테스트 7 〉	통과 (229.69ms, 83MB)
테스트 8 〉	통과 (342.05ms, 82.4MB)
테스트 9 〉	통과 (2127.81ms, 98.5MB)
테스트 10 〉	실패 (시간 초과)
테스트 11 〉	실패 (시간 초과)
테스트 12 〉	실패 (시간 초과)
테스트 13 〉	실패 (시간 초과)
테스트 14 〉	실패 (시간 초과)
테스트 15 〉	실패 (시간 초과)
테스트 16 〉	실패 (시간 초과)
테스트 17 〉	통과 (338.21ms, 198MB)
테스트 18 〉	통과 (2.29ms, 78.3MB)
테스트 19 〉	통과 (3.32ms, 71.2MB)
테스트 20 〉	통과 (3.47ms, 73.5MB)
테스트 21 〉	통과 (3.48ms, 77.1MB)
테스트 22 〉	통과 (3.36ms, 78MB)
테스트 23 〉	통과 (3.23ms, 72.7MB)
테스트 24 〉	실패 (시간 초과)
테스트 25 〉	실패 (시간 초과)
테스트 26 〉	실패 (시간 초과)
테스트 27 〉	실패 (시간 초과)
테스트 28 〉	실패 (시간 초과)
테스트 29 〉	실패 (시간 초과)
테스트 30 〉	실패 (시간 초과)
테스트 31 〉	통과 (2.71ms, 76.9MB)
테스트 32 〉	통과 (2.64ms, 73.4MB)
테스트 33 〉	통과 (2.19ms, 75.2MB)
테스트 34 〉	통과 (2.35ms, 73.7MB)

수정된 코드


문제의 경우 정렬된 수열이 주어지므로, 투 포인터를 이용하면 문제를 해결 할 수 있을것이라고 판단했다. 

start, end 인덱스를 가지는 변수를 선언한 뒤, 덧셈한 값이 k보다 작으면 end 인덱스의 값을 더하고, end 인덱스를 늘려준다. 만약 덧셈한 값이 k 보다 크다면, start 인덱스의 값을 빼주고, start 인덱스를 한칸 올려준다.

연산이 끝나면, 가장 길이가 짧은 수열을 리턴해야 하므로 정렬 후 리턴해줬다. 

코드

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

class Solution {
    public int[] solution(int[] sequence, int k) {
        int[] answer;
        ArrayList<int[]> list = new ArrayList<>();
        // k 보다큰 값 제외
        int sum = 0;
        int start = 0;
        int end  = 0;
        while (start <= end && end < sequence.length) {
            if (sum == k) {
                list.add(new int[]{start,end-1});
            }
            if (sum + sequence[end] <= k) {
                sum += sequence[end++];
            } else {
                sum -= sequence[start++];
            }
        }
        if (sum == k) {
            list.add(new int[]{start,end-1});
        }
        list.sort(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return (o1[1]- o1[0]) - (o2[1] - o2[0]);
            }
        });

        answer = list.get(0);
        return answer;
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 (추가 시간 없음) 1024 MB 11094 4316 2704 35.514%

문제

상어 중학교의 코딩 동아리에서 게임을 만들었다. 이 게임은 크기가 N×N인 격자에서 진행되고, 초기에 격자의 모든 칸에는 블록이 하나씩 들어있고, 블록은 검은색 블록, 무지개 블록, 일반 블록이 있다. 일반 블록은 M가지 색상이 있고, 색은 M이하의 자연수로 표현한다. 검은색 블록은 -1, 무지개 블록은 0으로 표현한다. (i, j)는 격자의 i번 행, j번 열을 의미하고, |r1 - r2| + |c1 - c2| = 1을 만족하는 두 칸 (r1, c1)과 (r2, c2)를 인접한 칸이라고 한다.

블록 그룹은 연결된 블록의 집합이다. 그룹에는 일반 블록이 적어도 하나 있어야 하며, 일반 블록의 색은 모두 같아야 한다. 검은색 블록은 포함되면 안 되고, 무지개 블록은 얼마나 들어있든 상관없다. 그룹에 속한 블록의 개수는 2보다 크거나 같아야 하며, 임의의 한 블록에서 그룹에 속한 인접한 칸으로 이동해서 그룹에 속한 다른 모든 칸으로 이동할 수 있어야 한다. 블록 그룹의 기준 블록은 무지개 블록이 아닌 블록 중에서 행의 번호가 가장 작은 블록, 그러한 블록이 여러개면 열의 번호가 가장 작은 블록이다.

오늘은 이 게임에 오토 플레이 기능을 만드려고 한다. 오토 플레이는 다음과 같은 과정이 블록 그룹이 존재하는 동안 계속해서 반복되어야 한다.

  1. 크기가 가장 큰 블록 그룹을 찾는다. 그러한 블록 그룹이 여러 개라면 포함된 무지개 블록의 수가 가장 많은 블록 그룹, 그러한 블록도 여러개라면 기준 블록의 행이 가장 큰 것을, 그 것도 여러개이면 열이 가장 큰 것을 찾는다.
  2. 1에서 찾은 블록 그룹의 모든 블록을 제거한다. 블록 그룹에 포함된 블록의 수를 B라고 했을 때, B점을 획득한다.
  3. 2
  4. 격자에 중력이 작용한다.
  5. 격자가 90도 반시계 방향으로 회전한다.
  6. 다시 격자에 중력이 작용한다.

격자에 중력이 작용하면 검은색 블록을 제외한 모든 블록이 행의 번호가 큰 칸으로 이동한다. 이동은 다른 블록이나 격자의 경계를 만나기 전까지 계속 된다.

다음은 N = 5, M = 3인 경우의 예시이다.

2 2 -1 3 1

3 3 2 0 -1
0 0 0 1 2
-1 3 1 3 2
0 3 2 2 1

여기서 찾을 수 있는 크기가 가장 큰 블록 그룹을 다음과 같이 빨간색으로 표시했다.

2 2 -1 3 1

3 3 2 0 -1
0 0 0 1 2
-1 3 1 3 2
0 3 2 2 1

블록 그룹이 제거되면 다음과 같이 변하고, 점수 82점을 획득한다.

2 2 -1 3 1

    2 0 -1
      1 2
-1   1 3 2
    2 2 1

중력이 작용하면 다음과 같이 변한다.

-1 3 1

      0 -1
2   2 1 2
-1   1 3 2
  2 2 2 1

90도 반시계방향으로 회전한 결과는 다음과 같다.

1 -1 2 2 1

3 0 1 3 2
-1   2 1 2
        2
    2 -1  

다시 여기서 중력이 작용하면 다음과 같이 변한다.

1 -1

3   2 2 1
-1   1 3 2
    2 1 2
  0 2 -1 2

오토 플레이가 모두 끝났을 때 획득한 점수의 합을 구해보자.

입력

첫째 줄에 격자 한 변의 크기 N, 색상의 개수 M이 주어진다.

둘째 줄부터 N개의 줄에 격자의 칸에 들어있는 블록의 정보가 1번 행부터 N번 행까지 순서대로 주어진다. 각 행에 대한 정보는 1열부터 N열까지 순서대로 주어진다. 입력으로 주어지는 칸의 정보는 -1, 0, M이하의 자연수로만 이루어져 있다.

출력

첫째 줄에 획득한 점수의 합을 출력한다.

제한

  • 1 ≤ N ≤ 20
  • 1 ≤ M ≤ 5

풀이

소요 시간

100분

문제에 제시된 오토 플레이 기능을 그대로 구현하면 된다.

  1. BFS를 통해 가장 큰 블록 그룹을 구한다.
  2. 블록 그룹을 제거한다. (EMPTY_BLOCK 으로 마킹했다.)
  3. 중력을 적용한다
  4. 90도 반시계 방향으로 회전시킨다.
  5. 중력을 적용한다.
  6. 1~5를 반복한다. 만약 1번 수행 후 가장 큰 블록 그룹의 블록 갯수가 2개 미만이라면 종료한다.

헤맸던 부분

블록 그룹의 기준 블록은 무지개 블록이 아닌 블록 중에서 행의 번호가 가장 작은 블록, 그러한 블록이 여러개면 열의 번호가 가장 작은 블록이다.

이거 잘못 읽어서 행 길이, 열 길이로 구했다가 한 20분 날려먹었다 문제 잘읽자..

코드

package com.company.baekjoon;

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

public class B21609 {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};

    static int N;
    static int M;
    static int[][] arr;
    static boolean[][] visited;
    static int answer = 0;

    static int cnt = 0; // 블록 갯수
    static int rainbowCnt = 0; // 무지개 블록 갯수
    static int targetI = 0; // 행 번호
    static int targetJ = 0; // 열 번호
    static int target = 0;
    static int EMPTY_BLOCK = -2;
    static ArrayList<int[]> targetList = new ArrayList<>();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

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

        arr = new int[N][N];
        visited = new boolean[N][N];

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

        while (true) {
            visited = new boolean[N][N];
            cnt = 0; // 블록 갯수
            rainbowCnt = 0; // 무지개 블록 갯수
            targetI = 0; // 행 갯수
            targetJ = 0; // 열 갯수
            target = 0;
            targetList.clear();
            // BFS로 가장 큰 블록 탐색
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (!visited[i][j] && arr[i][j] != -1 && arr[i][j] != 0 && arr[i][j] != EMPTY_BLOCK) {
                        visited[i][j] = true;
                        bfs(i,j);

                    }
                }
            }

            if (cnt < 2) break;

            answer += cnt*cnt;

            // 가장 큰 블록 없애기
            setBlockEmpty();

            // 블록에 중력 적용하기
            gravity();

            // 90도 반시계 방향 회전
            rotation();

            // 블록에 중력 적용하기
            gravity();
        }
        System.out.println(answer);
    }
    public static void bfs(int i, int j) {
        int cCnt = 1; // 블록 갯수
        int cRainbowCnt = 0; // 무지개 블록 갯수
        int cTargetI = i; // 행 번호
        int cTargetJ = j; // 열 번호
        int cTarget = arr[i][j];
        ArrayList<int[]> cTargetList = new ArrayList<>();
        cTargetList.add(new int[] {i,j});
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[] {i,j});
        while (!queue.isEmpty()) {
            int[] value = queue.poll();

            for (int k = 0; k < dx.length; k++) {
                int cx = value[0] + dx[k];
                int cy = value[1] + dy[k];

                if (cx < 0 || cy < 0 || cx >= N || cy >= N) continue;
                if (visited[cx][cy]) continue;
                if (arr[i][j] == arr[cx][cy]) {
                    cCnt++;
                    queue.add(new int[] {cx, cy});
                    cTargetList.add(new int[] {cx,cy});
                    visited[cx][cy] = true;
                } else if (arr[cx][cy] == 0) {
                    cCnt++;
                    cRainbowCnt++;
                    queue.add(new int[] {cx, cy});
                    cTargetList.add(new int[] {cx,cy});
                    visited[cx][cy] = true;
                }
            }
        }
        if (cTargetList.size() < 2) return;
        selectTarget(cCnt, cRainbowCnt, cTargetI, cTargetJ, cTarget, cTargetList);

        for (int k = 0; k < cTargetList.size(); k++) {
            // 타겟이 선정되고 난 후 값이 0인 위치는 공유 가능하므로 다시 visited를 false로 변경해준다.
            int[] t = cTargetList.get(k);
            if (arr[t[0]][t[1]] == 0)
                visited[t[0]][t[1]] = false;
        }

    }
    public static void selectTarget(int cCnt, int cRainbowCnt, int cTargetI, int cTargetJ, int cTarget, ArrayList<int[]> cTargetList) {
        if (cCnt> cnt) {
            cnt = cCnt;
            rainbowCnt = cRainbowCnt;
            targetI = cTargetI;
            targetJ = cTargetJ;
            target = cTarget;
            targetList = cTargetList;
        } else if (cCnt == cnt) {
            if (cRainbowCnt > rainbowCnt) {
                cnt = cCnt;
                rainbowCnt = cRainbowCnt;
                targetI = cTargetI;
                targetJ = cTargetJ;
                target = cTarget;
                targetList = cTargetList;
            } else if (cRainbowCnt == rainbowCnt) {
                if (cTargetI > targetI) {
                    cnt = cCnt;
                    rainbowCnt = cRainbowCnt;
                    targetI = cTargetI;
                    targetJ = cTargetJ;
                    target = cTarget;
                    targetList = cTargetList;
                } else if (cTargetI == targetI) {
                    if (cTargetJ > targetJ) {
                        cnt = cCnt;
                        rainbowCnt = cRainbowCnt;
                        targetI = cTargetI;
                        targetJ = cTargetJ;
                        target = cTarget;
                        targetList = cTargetList;
                    }
                }
            }
        }
    }
    public static void setBlockEmpty() {
        for (int i = 0; i < targetList.size(); i++) {
            int[] target = targetList.get(i);
            arr[target[0]][target[1]] = EMPTY_BLOCK;
        }
    }
    public static void gravity() {
        for(int i = 0; i < N; ++i)
        {
            for(int j = N-1; j >= 0; --j)
            {
                if(arr[j][i] == EMPTY_BLOCK || arr[j][i] == -1) continue;
                int idx = j+1;
                while(true)
                {
                    if(idx == N) break;
                    if(arr[idx][i] == EMPTY_BLOCK) idx++;
                    else break;
                }
                if(idx == j+1) continue;
                arr[idx-1][i] = arr[j][i];
                arr[j][i] = EMPTY_BLOCK;
            }
        }
    }
    public static void rotation() {
        int [][] tmp = new int[N][N];
        for(int i = 0; i < N; ++i)
        {
            for(int j = 0; j < N; ++j)
            {
                tmp[N-j-1][i] = arr[i][j];
            }
        }
        for(int i = 0; i < N; ++i)
        {
            System.arraycopy(tmp[i],0,arr[i],0,N);
        }
    }
}
  • 귤 고르기
문제 설명

경화는 과수원에서 귤을 수확했습니다. 경화는 수확한 귤 중 'k'개를 골라 상자 하나에 담아 판매하려고 합니다. 그런데 수확한 귤의 크기가 일정하지 않아 보기에 좋지 않다고 생각한 경화는 귤을 크기별로 분류했을 때 서로 다른 종류의 수를 최소화하고 싶습니다.

예를 들어, 경화가 수확한 귤 8개의 크기가 [1, 3, 2, 5, 4, 5, 2, 3] 이라고 합시다. 경화가 귤 6개를 판매하고 싶다면, 크기가 1, 4인 귤을 제외한 여섯 개의 귤을 상자에 담으면, 귤의 크기의 종류가 2, 3, 5로 총 3가지가 되며 이때가 서로 다른 종류가 최소일 때입니다.

경화가 한 상자에 담으려는 귤의 개수 k와 귤의 크기를 담은 배열 tangerine이 매개변수로 주어집니다. 경화가 귤 k개를 고를 때 크기가 서로 다른 종류의 수의 최솟값을 return 하도록 solution 함수를 작성해주세요.


제한사항
  • 1 ≤ k  tangerine의 길이 ≤ 100,000
  • 1 ≤ tangerine의 원소 ≤ 10,000,000

입출력 예ktangerineresult
6 [1, 3, 2, 5, 4, 5, 2, 3] 3
4 [1, 3, 2, 5, 4, 5, 2, 3] 2
2 [1, 1, 1, 1, 2, 2, 2, 3] 1

입출력 예 설명

입출력 예 #1

  • 본문에서 설명한 예시입니다.

입출력 예 #2

  • 경화는 크기가 2인 귤 2개와 3인 귤 2개 또는 2인 귤 2개와 5인 귤 2개 또는 3인 귤 2개와 5인 귤 2개로 귤을 판매할 수 있습니다. 이때의 크기 종류는 2가지로 이 값이 최소가 됩니다.

입출력 예 #3

  • 경화는 크기가 1인 귤 2개를 판매하거나 2인 귤 2개를 판매할 수 있습니다. 이때의 크기 종류는 1가지로, 이 값이 최소가 됩니다.

 

 

나의 풀이

 

귤을 크기별로 분류했을 때 서로 다른 종류의 수를 최소화

최대한 사이즈가 같은 귤이 많게 해야함.

따라서, 귤을 중복이 많은 순으로 정렬하여 갯수만큼 빼면 답을 얻을 수 있다.

ex) k = 3, 귤 = [1, 2, 2, 3, 4, 4] 일 경우

중복이 많은 순으로 정렬 시

귤 = [4, 4, 2, 2, 3, 1] 으로 정렬 됨.

k 에서 사이즈가 같은 귤의 갯수 만큼 빼나가는 중 k가 0보다 작아질 때 종류가 가장 작은 경우가 됨.
 
class Solution {
    fun solution(k: Int, tangerine: IntArray): Int {
        var answer: Int = 0
        var ans_k = k
        var list: MutableList<Int> = ArrayList() // 귤 갯수를 담음
        var set: Set<Int> = tangerine.toSet() // 귤 종류를 담음
        for(i in set) {
            val cnt = tangerine.count { it == i} // 귤 종류에서 중복되는 갯수를 리스트에 담기
            list.add(cnt)
        }
        list.sortDescending() // 갯수가 많은순으로 정렬
        for (i in list) {
            if (ans_k <= 0 ) 
              return answer
            ans_k -= i // 판매할만큼 빼주기
            answer++
        }
        return answer
    }
}

코틀린이랑 친해지기 위해서 문제를 풀어봤다.. 종종 풀어봐야징

문제 설명

개발자가 사용하는 언어와 언어 선호도를 입력하면 그에 맞는 직업군을 추천해주는 알고리즘을 개발하려고 합니다.

아래 표는 5개 직업군 별로 많이 사용하는 5개 언어에 직업군 언어 점수를 부여한 표입니다.

점수SI CONTENTS HARDWARE PORTAL GAME

5 JAVA JAVASCRIPT C JAVA C++
4 JAVASCRIPT JAVA C++ JAVASCRIPT C#
3 SQL PYTHON PYTHON PYTHON JAVASCRIPT
2 PYTHON SQL JAVA KOTLIN C
1 C# C++ JAVASCRIPT PHP JAVA

예를 들면, SQL의 SI 직업군 언어 점수는 3점이지만 CONTENTS 직업군 언어 점수는 2점입니다. SQL의 HARDWARE, PORTAL, GAME 직업군 언어 점수는 0점입니다.

직업군 언어 점수를 정리한 문자열 배열 table, 개발자가 사용하는 언어를 담은 문자열 배열 languages, 언어 선호도를 담은 정수 배열 preference가 매개변수로 주어집니다. 개발자가 사용하는 언어의 언어 선호도 x 직업군 언어 점수의 총합이 가장 높은 직업군을 return 하도록 solution 함수를 완성해주세요. 총합이 같은 직업군이 여러 개일 경우, 이름이 사전 순으로 가장 빠른 직업군을 return 해주세요.


제한사항

  • table의 길이 = 5
    • table의 원소는 "직업군 5점언어 4점언어 3점언어 2점언어 1점언어"형식의 문자열입니다. 직업군, 5점언어, 4언어, 3점언어, 2점언어, 1점언어는 하나의 공백으로 구분되어 있습니다.
    • table은 모든 테스트케이스에서 동일합니다.
  • 1 ≤ languages의 길이 ≤ 9
    • languages의 원소는 "JAVA", "JAVASCRIPT", "C", "C++" ,"C#" , "SQL", "PYTHON", "KOTLIN", "PHP" 중 한 개 이상으로 이루어져 있습니다.
    • languages의 원소는 중복되지 않습니다.
  • preference의 길이 = languages의 길이
    • 1 ≤ preference의 원소 ≤ 10
  • preference의 i번째 원소는 languages의 i번째 원소의 언어 선호도입니다.
  • return 할 문자열은 "SI", "CONTENTS", "HARDWARE", "PORTAL", "GAME" 중 하나입니다.

입출력 예

tablelanguagespreferenceresult

["SI JAVA JAVASCRIPT SQL PYTHON C#", "CONTENTS JAVASCRIPT JAVA PYTHON SQL C++", "HARDWARE C C++ PYTHON JAVA JAVASCRIPT", "PORTAL JAVA JAVASCRIPT PYTHON KOTLIN PHP", "GAME C++ C# JAVASCRIPT C JAVA"] ["PYTHON", "C++", "SQL"] [7, 5, 5] "HARDWARE"
["SI JAVA JAVASCRIPT SQL PYTHON C#", "CONTENTS JAVASCRIPT JAVA PYTHON SQL C++", "HARDWARE C C++ PYTHON JAVA JAVASCRIPT", "PORTAL JAVA JAVASCRIPT PYTHON KOTLIN PHP", "GAME C++ C# JAVASCRIPT C JAVA"] ["JAVA", "JAVASCRIPT"] [7, 5] "PORTAL"

입출력 예 설명

입출력 예 #1

각 직업군 별로 점수를 계산해보면 아래와 같습니다.

아래 사진은 개발자 언어 선호도 나타낸 표입니다.

아래 사진은 개발자가 선호하는 언어의 직업군 언어 점수를 나타낸 표입니다.

따라서 점수 총합이 41로 가장 높은 "HARDWARE"를 return 해야 합니다.

입출력 예 #2

각 직업군 별로 점수를 계산해보면 아래와 같습니다.

아래 사진은 개발자 언어 선호도 나타낸 표입니다.

아래 사진은 개발자가 선호하는 언어의 직업군 언어 점수를 나타낸 표입니다.


점수 총합이 55로 가장 높은 직업군은 "SI" 와 "PORTAL"입니다.
따라서 사전 순으로 먼저 오는 "PORTAL"을 return 해야 합니다.

 

나의 풀이

첫번째 파라미터로 오는 배열을 보면 첫번째 값에는 직업군, 그 이후에는 언어가 오게되므로 우선 하나의 문자열을 분리시켰다.

그 이후 점수 총합과 직업군을 저장하기위해 key, value로 값을 저장하는 hashMap을 사용하여 점수 총합과 직업을 저장했다.

만약 점수가 같은 직업이 있다면 정렬을 통해 사전순서가 빠른 직업군을 리턴하도록 했다.

 

코틀린 언어가 익숙하지 않아서 조금 헤매었는데 풀고나니 조타 ㅎㅅㅎ

+ 프로그래머스와 내 로컬환경의 컴파일옵션이 달라서 그런지 내 노트북에서는 잘 컴파일 되었는데 프로그래머스에서 실행하면

오류가 많이 떠서 수정하는데 시간이 좀 걸렸다. 

import kotlin.collections.HashMap

class Solution {
   fun solution(table: Array<String>, languages: Array<String>, preference: IntArray): String {

        var hashMap = HashMap<String, Int>()
        for (i in table.indices) { // 첫번째 배열 아이템 나누기
            var tableArr = table[i].split(" ")
            var sum = 0

            for (j in languages.indices) {
                var idx = tableArr.indexOf(languages[j])
                if (idx != -1) {
                    var size = tableArr.size - idx
                    sum += size * preference[j]
                }
            }
            hashMap.put(tableArr[0],sum)
        }
        var maxVal = hashMap.values.max()
        var answers = hashMap.filter { it.value == maxVal }
        return answers.keys.min()!!
    }
}

문제 설명

대학 교수인 당신은, 상호평가를 통하여 학생들이 제출한 과제물에 학점을 부여하려고 합니다. 아래는 0번부터 4번까지 번호가 매겨진 5명의 학생들이 자신과 다른 학생의 과제를 평가한 점수표입니다.

No. 0 1 2 3 4
0 100 90 98 88 65
1 50 45 99 85 77
2 47 88 95 80 67
3 61 57 100 80 65
4 24 90 94 75 65
평균 45.5 81.25 97.2 81.6 67.8
학점 F B A B D

위의 점수표에서, i행 j열의 값은 i번 학생이 평가한 j번 학생의 과제 점수입니다.

  • 0번 학생이 평가한 점수는 0번 행에담긴 [100, 90, 98, 88, 65]입니다.
    • 0번 학생은 자기 자신에게 100점, 1번 학생에게 90점, 2번 학생에게 98점, 3번 학생에게 88점, 4번 학생에게 65점을 부여했습니다.
  • 2번 학생이 평가한 점수는 2번 행에담긴 [47, 88, 95, 80, 67]입니다.
    • 2번 학생은 0번 학생에게 47점, 1번 학생에게 88점, 자기 자신에게 95점, 3번 학생에게 80점, 4번 학생에게 67점을 부여했습니다.

당신은 각 학생들이 받은 점수의 평균을 구하여, 기준에 따라 학점을 부여하려고 합니다.
만약, 학생들이 자기 자신을 평가한 점수가 유일한 최고점 또는 유일한 최저점이라면 그 점수는 제외하고 평균을 구합니다.

  • 0번 학생이 받은 점수는 0번 열에 담긴 [100, 50, 47, 61, 24]입니다. 자기 자신을 평가한 100점은 자신이 받은 점수 중에서 유일한 최고점이므로, 평균을 구할 때 제외합니다.
    • 0번 학생의 평균 점수는 (50+47+61+24) / 4 = 45.5입니다.
  • 4번 학생이 받은 점수는 4번 열에 담긴 [65, 77, 67, 65, 65]입니다. 자기 자신을 평가한 65점은 자신이 받은 점수 중에서 최저점이지만 같은 점수가 2개 더 있으므로, 유일한 최저점이 아닙니다. 따라서, 평균을 구할 때 제외하지 않습니다.
    • 4번 학생의 평균 점수는 (65+77+67+65+65) / 5 = 67.8입니다.

제외할 점수는 제외하고 평균을 구한 후, 아래 기준에 따라 학점을 부여합니다.

평균학점

90점 이상 A
80점 이상 90점 미만 B
70점 이상 80점 미만 C
50점 이상 70점 미만 D
50점 미만 F

학생들의 점수가 담긴 정수형 2차원 배열 scores가 매개변수로 주어집니다. 이때, 학생들의 학점을 구하여 하나의 문자열로 만들어서 return 하도록 solution 함수를 완성해주세요.


제한사항

  • 2 ≤ scores의 행의 길이(학생 수) ≤ 10
  • scores의 열의 길이 = scores의 행의 길이
    • 즉, scores는 행과 열의 길이가 같은 2차원 배열입니다.
  • 0 ≤ scores의 원소 ≤ 100
  • return 값 형식
    • 0번 학생의 학점부터 차례대로 이어 붙인 하나의 문자열을 return 합니다.

입출력 예

scoresresult

[[100,90,98,88,65],[50,45,99,85,77],[47,88,95,80,67],[61,57,100,80,65],[24,90,94,75,65]] "FBABD"
[[50,90],[50,87]] "DA"
[[70,49,90],[68,50,38],[73,31,100]] "CFD"

입출력 예 설명

입출력 예 #1

문제 예시와 같습니다.

입출력 예 #2

No. 0 1
0 50 90
1 50 87
평균 50 90
학점 D A
  • 1번 학생이 자기 자신을 평가한 87점은 [90, 87]에서 유일한 최저점이므로, 평균을 구할 때 제외합니다.

입출력 예 #3

No. 0 1 2
0 70 49 90
1 68 50 38
2 73 31 100
평균 70.33… 40 64
학점 C F D
  • 1번 학생이 자기 자신을 평가한 50점은 [49, 50, 31]에서 유일한 최고점이므로, 평균을 구할 때 제외합니다.
  • 2번 학생이 자기 자신을 평가한 100점은 [90, 38, 100]에서 유일한 최고점이므로, 평균을 구할 때 제외합니다.

 

나의 풀이

 

1. 2차원 배열의 행 열을 변경하기 -> 코틀린 배열에서 제공하는 max, min 함수 사용을 위해

2. 자기 자신에게 평가 한 경우 max 또는 min 값인지 확인 후에 유일한 최저점, 최고점 확인 

 

class Solution {
     fun solution(scores: Array<IntArray>): String {
        var answer: String = ""
        var rotatedScores : Array<IntArray> = Array(scores.size) { IntArray(scores.size)}

        for ( i in scores.indices)
            for (j in scores[i].indices)
                rotatedScores[i][j] = scores[j][i]

        for (i in scores.indices)
        {
            var sum = 0.0F
            var cnt = rotatedScores[i].size

            for ( j in rotatedScores[i].indices)
            {
                if (i == j)
                {
                    if (rotatedScores[i][j] == rotatedScores[j].min() ||
                            rotatedScores[i][j] == rotatedScores[j].max())
                    {
                        var countOfSameVal = rotatedScores[i].filter { it.equals(rotatedScores[i][j])}
                        if (countOfSameVal.size < 2) {
                            cnt -= 1
                            continue
                        }
                    }
                }
                sum += rotatedScores[i][j]
            }
            sum /= cnt

            when (sum) {
                in 90F..100F -> answer = answer.plus("A")
                in 80F..90F -> answer = answer.plus("B")
                in 70F..80F -> answer = answer.plus("C")
                in 50F..70F -> answer = answer.plus("D")
                in  0F..50F -> answer = answer.plus("F")
            }
        }
        return answer
    }
}

코드는 생각보다 길어졌지만 코틀린에 익숙해지는 것을 목표로 해본다 ㅠ

  • 부족한 금액 계산하기

문제 설명

새로 생긴 놀이기구는 인기가 매우 많아 줄이 끊이질 않습니다. 이 놀이기구의 원래 이용료는 price원 인데, 놀이기구를 N 번 째 이용한다면 원래 이용료의 N배를 받기로 하였습니다. 즉, 처음 이용료가 100이었다면 2번째에는 200, 3번째에는 300으로 요금이 인상됩니다.
놀이기구를 count번 타게 되면 현재 자신이 가지고 있는 금액에서 얼마가 모자라는지를 return 하도록 solution 함수를 완성하세요.
단, 금액이 부족하지 않으면 0을 return 하세요.

제한사항

  • 놀이기구의 이용료 price : 1 ≤ price ≤ 2,500, price는 자연수
  • 처음 가지고 있던 금액 money : 1 ≤ money ≤ 1,000,000,000, money는 자연수
  • 놀이기구의 이용 횟수 count : 1 ≤ count ≤ 2,500, count는 자연수

입출력 예

pricemoneycountresult

3 20 4 10

입출력 예 설명

입출력 예 #1
이용금액이 3인 놀이기구를 4번 타고 싶은 고객이 현재 가진 금액이 20이라면, 총 필요한 놀이기구의 이용 금액은 30 (= 3+6+9+12) 이 되어 10만큼 부족하므로 10을 return 합니다.

참고 사항

  • 미션 언어는 Java, JavaScript, Python3, C++ 만 해당 됩니다.
  • 같은 코드를 제출한 사람이 여럿이라면 코드를 가장 먼저 제출한 분께 상품을 드립니다.
  • 좋아요 수가 동일할 경우 코드를 가장 먼저 제출한 분께 상품을 드립니다.

 

나의 풀이

 

문제는 간단했는데, 네가지 테스트가 계속 실패하는 바람에.. 도무지 모르겠어서 구글링 했더니 money값을 받을 때 

int형이 아닌 long형으로 받아야 한다고 해서 매개변수 타입을 long으로 변경하니 성공했다.. 문제를 보면 알 수 있는 부분인데 좀더 문제를 정확히 읽고 풀어야겠다 ㅠ-ㅠ

    public long solution(int price, long money, int count) {
        for(int i = 1; i <= count; i++)  
            money = money - price * i;
        return  money > 0 ? 0 : money*-1;
    }

+ Recent posts