문제
크기가 N×M인 배열이 있을 때, 배열을 돌려보려고 한다. 배열은 다음과 같이 반시계 방향으로 돌려야 한다.
A[1][1] ← A[1][2] ← A[1][3] ← A[1][4] ← A[1][5]
↓ ↑
A[2][1] A[2][2] ← A[2][3] ← A[2][4] A[2][5]
↓ ↓ ↑ ↑
A[3][1] A[3][2] → A[3][3] → A[3][4] A[3][5]
↓ ↑
A[4][1] → A[4][2] → A[4][3] → A[4][4] → A[4][5]
예를 들어, 아래와 같은 배열을 2번 회전시키면 다음과 같이 변하게 된다.
1 2 3 4 2 3 4 8 3 4 8 6
5 6 7 8 1 7 7 6 2 7 8 2
9 8 7 6 → 5 6 8 2 → 1 7 6 3
5 4 3 2 9 5 4 3 5 9 5 4
<시작> <회전1> <회전2>
배열과 정수 R이 주어졌을 때, 배열을 R번 회전시킨 결과를 구해보자.
입력
첫째 줄에 배열의 크기 N, M과 수행해야 하는 회전의 수 R이 주어진다.
둘째 줄부터 N개의 줄에 배열 A의 원소 Aij가 주어진다.
출력
입력으로 주어진 배열을 R번 회전시킨 결과를 출력한다.
풀이
설명을 위해, 배열에 대하여 layer을 다음과 같이 정의한다:
1 | 1 | 1 | 1 | 1 | 1 |
1 | 2 | 2 | 2 | 2 | 1 |
1 | 2 | 3 | 3 | 2 | 1 |
1 | 2 | 3 | 3 | 2 | 1 |
1 | 2 | 2 | 2 | 2 | 1 |
1 | 1 | 1 | 1 | 1 | 1 |
같은 숫자로 표시한 곳이 같은 layer라고 하자. 위와 같은 방식으로 M*N 배열에서도 layer를 정의할 수 있다.
풀이 1. 빡구현으로(index에서 실수하기 매우 쉬움!) 배열에서 돌려서 푼다. 각 껍질(아래 표 참고)마다 R을 (2*가로길이 + 2*세로길이 - 4)로 나눈 나머지만큼 회전시킨다. 왜냐? 주기가 (2*가로길이 + 2*세로길이 - 4)이기 때문이다. 모듈러 계산만 했다면 시간 내로 풀릴 것이다.
풀이 2. 회전 한 번이 (2*가로길이 + 2*세로길이 - 4)만큼의 연산이 들어간다는 점이 비효율적으로 느껴져서, 각 layer의 원소를 큐에 넣어준 후, queue에서 dequeue한 값을 enqueue하면 회전 한 번에 연산 2번으로 끝날 것이라 생각했다. 일단 배열을 저장한 후, 각 layer를 따라 각각의 원소를 올바른 queue에 넣어준 후, 돌리는 연산을 수행한 후(마찬가지로 R을 (2*가로길이 + 2*세로길이 - 4)로 나눈 나머지만큼만 돌려준다.), 각 layer를 따라 큐에서 dequeue하여 회전 연산한 값을 배열에 넣어주었다.
풀이 1이 Worst Case O(MN*max(M, N))이고, 풀이 2는 Worst case O(MN) time complexity를 가질 것이다.
코드
풀이 1
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static int[][] array;
public static void main(String[] args) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int M = Integer.parseInt(st.nextToken());
int N = Integer.parseInt(st.nextToken());
long R = Long.parseLong(st.nextToken());
int start = 0;
array = new int[M][N];
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
array[i][j] = Integer.parseInt(st.nextToken());
}
}
while (2*start + 1 < M && 2*start + 1 < N) {
for (int i = 0; i < (R % (2*M+2*N-8*start-4)); i++) {
rotate(start, M-start, N-start);
}
start++;
}
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
sb.append(array[i][j]+" ");
}
sb.append("\n");
}
sb.setLength(sb.length()-1);
System.out.println(sb);
}
public static void rotate(int start, int M, int N) {
int temp = array[start][start];
for (int j = start+1; j < N; j++) {
array[start][j-1] = array[start][j];
}
for (int i = start+1; i < M; i++) {
array[i-1][N-1] = array[i][N-1];
}
for (int j = N-2; j > start-1; j--) {
array[M-1][j+1] = array[M-1][j];
}
for (int i = M-2; i > start; i--) {
array[i+1][start] = array[i][start];
}
array[start+1][start] = temp;
}
}
풀이 2
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
static List<ArrayDeque<Integer>> queueList = new ArrayList<ArrayDeque<Integer>>();
static int[][] array;
public static void main(String[] args) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int M = Integer.parseInt(st.nextToken());
int N = Integer.parseInt(st.nextToken());
long R = Long.parseLong(st.nextToken());
int start = 0;
array = new int[M][N];
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
array[i][j] = Integer.parseInt(st.nextToken());
}
}
while (2*start + 1 < M && 2*start + 1 < N) {
ArrayDeque<Integer> temp = new ArrayDeque<Integer>();
for (int j = start; j < N - start; j++) {
temp.offer(array[start][j]);
}
for (int i = start+1; i < M - start; i++) {
temp.offer(array[i][N-1-start]);
}
for (int j = N-2-start; j > start-1; j--) {
temp.offer(array[M-1-start][j]);
}
for (int i = M-2-start; i > start; i--) {
temp.offer(array[i][start]);
}
queueList.add(temp);
start++;
}
start = 0;
while (2*start + 1 < M && 2*start + 1 < N) {
rotate(start, (R % (2*M + 2*N - 8*start - 4)));
start++;
}
start = 0;
while (2*start + 1 < M && 2*start + 1 < N) {
for (int j = start; j < N - start; j++) {
array[start][j] = queueList.get(start).poll();
}
for (int i = start+1; i < M - start; i++) {
array[i][N-1-start] = queueList.get(start).poll();
}
for (int j = N-2-start; j > start-1; j--) {
array[M-1-start][j] = queueList.get(start).poll();
}
for (int i = M-2-start; i > start; i--) {
array[i][start] = queueList.get(start).poll();
}
start++;
}
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
sb.append(array[i][j]+" ");
}
sb.append("\n");
}
System.out.println(sb);
}
public static void rotate(int index, long R) {
for (int i = 0; i < R; i++) {
queueList.get(index).offer(queueList.get(index).poll());
}
}
}
성능 비교
풀이 1이 572ms, 풀이 2가 424ms가 나왔다. 그렇게 유의미한 차이는 아니나, M, N이 커지면 더 차이가 날 거라 생각한다.
P.S.
배열 왜 N*M으로 제시하는 지 모르겠네.. 행렬은 M*N이지.
'PS > Implementation' 카테고리의 다른 글
백준 2527번: 직사각형 (JAVA) TODO (0) | 2022.02.09 |
---|---|
백준 2477: 참외밭 (JAVA) TODO (0) | 2022.02.09 |
백준 2669번: 직사각형 네 개의 합집합의 면적 구하기 (JAVA) (0) | 2022.01.28 |
백준 4307번: 개미 (Python) (0) | 2021.10.01 |
백준 1333번 (0) | 2021.10.01 |