[Programmers] Lv.2 - 행렬 테두리 회전하기 (Kotlin)

Dohyeon Ko·2021년 10월 17일
0

Programmers

목록 보기
7/8
post-thumbnail

문제 링크

문제 링크

문제 설명

rows x columns 크기인 행렬이 있습니다. 행렬에는 1부터 rows x columns까지의 숫자가 한 줄씩 순서대로 적혀있습니다. 이 행렬에서 직사각형 모양의 범위를 여러 번 선택해, 테두리 부분에 있는 숫자들을 시계방향으로 회전시키려 합니다. 각 회전은 (x1, y1, x2, y2)인 정수 4개로 표현하며, 그 의미는 다음과 같습니다.

x1 행 y1 열부터 x2 행 y2 열까지의 영역에 해당하는 직사각형에서 테두리에 있는 숫자들을 한 칸씩 시계방향으로 회전합니다.
다음은 6 x 6 크기 행렬의 예시입니다.

이 행렬에 (2, 2, 5, 4) 회전을 적용하면, 아래 그림과 같이 2행 2열부터 5행 4열까지 영역의 테두리가 시계방향으로 회전합니다. 이때, 중앙의 15와 21이 있는 영역은 회전하지 않는 것을 주의하세요.

행렬의 세로 길이(행 개수) rows, 가로 길이(열 개수) columns, 그리고 회전들의 목록 queries가 주어질 때, 각 회전들을 배열에 적용한 뒤, 그 회전에 의해 위치가 바뀐 숫자들 중 가장 작은 숫자들을 순서대로 배열에 담아 return 하도록 solution 함수를 완성해주세요.

제한사항

rows는 2 이상 100 이하인 자연수입니다.
columns는 2 이상 100 이하인 자연수입니다.
처음에 행렬에는 가로 방향으로 숫자가 1부터 하나씩 증가하면서 적혀있습니다.
즉, 아무 회전도 하지 않았을 때, i 행 j 열에 있는 숫자는 ((i-1) x columns + j)입니다.
queries의 행의 개수(회전의 개수)는 1 이상 10,000 이하입니다.
queries의 각 행은 4개의 정수 [x1, y1, x2, y2]입니다.
x1 행 y1 열부터 x2 행 y2 열까지 영역의 테두리를 시계방향으로 회전한다는 뜻입니다.
1 ≤ x1 < x2 ≤ rows, 1 ≤ y1 < y2 ≤ columns입니다.
모든 회전은 순서대로 이루어집니다.
예를 들어, 두 번째 회전에 대한 답은 첫 번째 회전을 실행한 다음, 그 상태에서 두 번째 회전을 실행했을 때 이동한 숫자 중 최솟값을 구하면 됩니다.

입출력 예시

rows columns queries result
6 6 [[2,2,5,4],[3,3,6,6],[5,1,6,3]][8, 10, 25]
3 3 [[1,1,2,2],[1,2,2,3],[2,1,3,2],[2,2,3,3]][1, 1, 5, 3]
100 97 [[1,1,100,97]][1]

풀이 언어

Kotlin

풀이 방법

이 문제를 처음 접했을 때는 초기 값이 저장되어 있는 배열을 새로운 배열에 저장해두고 회전을 시킬 때마다 계속 갱신해주는 방식으로 접근하려고 했다. 하지만 2차원 배열을 copy하는 방법을 결국 못찾아서 기존 배열을 갱신해주는 방식으로 접근했다. 전체적인 로직 자체는 비슷하다.

  • 시계 방향으로 돌면서 회전으로 움직이는 원소를 리스트에 저장한다. 이 때 회전시킬 원소 중에 최소값도 미리 판별한다.

  • 시계 방향으로 원소를 회전시키므로 위에서 선언한 리스트 로 보면 내부 원소가 오른쪽으로 한칸씩 이동했다고 보면 된다. 즉, 첫 번째 원소는 두 번째로, 두 번째 원소는 세 번째로... 최종적으로 마지막 원소가 첫 번째로 오게 된다.

  • (1) 에서 사용한 시계 방향으로 탐색하는 로직을 그대로 사용해서 기존 배열에 회전시킨 결과를 다시 넣어준다.

  • 위 과정을 queries 배열의 크기만큼 반복하고 한 턴을 돌 때마다 answer 배열에 추가해준다.

결과

코드

class Solution {
    // 최소값을 찾기 위한 메소드
    fun min(a : Int, b : Int) : Int {
        if (a >= b) {
            return b
        } else {
            return a
        }
    }
    
    fun solution(rows: Int, columns: Int, queries: Array<IntArray>): IntArray {
        var answer = intArrayOf()
        var array = Array (rows) {Array (columns) {0}}
        var cnt = 1
        
        for (row in 0 until rows) {
            for (col in 0 until columns) {
                array[row][col] = cnt
                cnt += 1
            }
        }
        
        for (i in queries.indices) {
            var srt = Pair<Int, Int> (queries[i][0] - 1, queries[i][1] - 1) // index를 0부터 시작하기 위해 -1
            var dst = Pair<Int, Int> (queries[i][2] - 1, queries[i][3] - 1)
            var element = arrayListOf<Int>()
            var minValue = 10000
        
            // 1. 위쪽 줄은 오른쪽으로 한 칸씩 이동   
            for (j in srt.second..dst.second - 1) {
                element.add(array[srt.first][j])
                minValue = min(minValue, array[srt.first][j])
            }
            
            // 2. 오른쪽 줄은 아래로 한 칸씩 이동
            for (j in srt.first..dst.first - 1) {
                element.add(array[j][dst.second])
                minValue = min(minValue, array[j][dst.second])
            }
            
            // 3. 아래쪽 줄은 왼쪽으로 한 칸씩 이동
            for (j in dst.second downTo srt.second + 1) {
                element.add(array[dst.first][j])
                minValue = min(minValue, array[dst.first][j])
            }
            
            // 4. 왼쪽 줄은 위로 한 칸씩 이동
            for (j in dst.first downTo srt.first + 1) {
                element.add(array[j][srt.second])
                minValue = min(minValue, array[j][srt.second])
            }
            
            // 5. 회전시킨 값 중 최소값 Plus
            answer = answer.plus(minValue)
            
            // 6. 시계 방향으로 원소 회전
            val temp = element.removeAt(element.size - 1)
            element.add(0, temp) 
            
            // 7. 다시 삽입, 삽입 방향은 위의 탐색 방향과 같은 시계 방향
            var index = 0
            for (j in srt.second..dst.second - 1) {
                array[srt.first][j] = element[index]
                index += 1
            }
            
            for (j in srt.first..dst.first - 1) {
                array[j][dst.second] = element[index]
                index += 1
            }
            
            for (j in dst.second downTo srt.second + 1) {
                array[dst.first][j] = element[index]
                index += 1
            }
            
            for (j in dst.first downTo srt.first + 1) {
                array[j][srt.second] = element[index]
                index += 1
            }
        }
        return answer
    }
}

알게 된 점

좀 고생한 문제였다. 로직 자체는 금방 찾아냈는데 초기 값을 저장하는 2차원 배열을 선언하는 방법과 2차원 배열을 copy 하는 법 을 못찾아서 엄청 고생했다. 결국 2차원 배열 copy 를 사용하지 않는 쪽으로 접근해서 해결했다. 1차원 배열은 copyOfclone 을 사용하면 깊은 복사가 잘 되는데 2차원은 왜 안되는지 모르겠다. 혹시 해결법 아시는 분 있으면 댓글 남겨주세요!

  • 배열과 리스트 모두 2차원으로 생성하고 싶을 경우 아래처럼 코드를 작성한다.
var array = Array (size) { Array (size) {0}}
var list = List (size) { List (size) {0}}
var mutableList = MutableList(size) { MutableList(size) {0}}
  • array 에서 removeAt 을 사용하면 특정 index 의 원소를 제거할 수 있다.

  • array 에서 add 를 사용할 때 index 도 넣어줄 수 있다. 매개변수 한 개만 넣어줄 때는 배열의 끝에 넣어줄 값 이고, 매개변수 두 개를 넣어줄 때는 (index, value) 이다.

profile
티스토리로 옮겼어요! (https://codekodo.tistory.com)

0개의 댓글