본문 바로가기

알고리즘/BOJ

[JAVA] BOJ(백준) - 배열 돌리기3 - 16935

문제내용

https://www.acmicpc.net/problem/16935

 

16935번: 배열 돌리기 3

크기가 N×M인 배열이 있을 때, 배열에 연산을 R번 적용하려고 한다. 연산은 총 6가지가 있다. 1번 연산은 배열을 상하 반전시키는 연산이다. 1 6 2 9 8 4 → 4 2 9 3 1 8 7 2 6 9 8 2 → 9 2 3 6 1 5 1 8 3 4 2 9 →

www.acmicpc.net

크기가 N×M인 배열이 있을 때, 배열에 연산을 R번 적용하려고 한다. 연산은 총 6가지가 있다.

1번 연산은 배열을 상하 반전시키는 연산이다.

1 6 2 9 8 4 → 4 2 9 3 1 8
7 2 6 9 8 2 → 9 2 3 6 1 5
1 8 3 4 2 9 → 7 4 6 2 3 1
7 4 6 2 3 1 → 1 8 3 4 2 9
9 2 3 6 1 5 → 7 2 6 9 8 2
4 2 9 3 1 8 → 1 6 2 9 8 4
   <배열>       <연산 결과>

2번 연산은 배열을 좌우 반전시키는 연산이다.

1 6 2 9 8 4 → 4 8 9 2 6 1
7 2 6 9 8 2 → 2 8 9 6 2 7
1 8 3 4 2 9 → 9 2 4 3 8 1
7 4 6 2 3 1 → 1 3 2 6 4 7
9 2 3 6 1 5 → 5 1 6 3 2 9
4 2 9 3 1 8 → 8 1 3 9 2 4
   <배열>       <연산 결과>

3번 연산은 오른쪽으로 90도 회전시키는 연산이다.

1 6 2 9 8 4 → 4 9 7 1 7 1
7 2 6 9 8 2 → 2 2 4 8 2 6
1 8 3 4 2 9 → 9 3 6 3 6 2
7 4 6 2 3 1 → 3 6 2 4 9 9
9 2 3 6 1 5 → 1 1 3 2 8 8
4 2 9 3 1 8 → 8 5 1 9 2 4
   <배열>       <연산 결과>

4번 연산은 왼쪽으로 90도 회전시키는 연산이다.

1 6 2 9 8 4 → 4 2 9 1 5 8
7 2 6 9 8 2 → 8 8 2 3 1 1
1 8 3 4 2 9 → 9 9 4 2 6 3
7 4 6 2 3 1 → 2 6 3 6 3 9
9 2 3 6 1 5 → 6 2 8 4 2 2
4 2 9 3 1 8 → 1 7 1 7 9 4
   <배열>       <연산 결과>

5, 6번 연산을 수행하려면 배열을 크기가 N/2×M/2인 4개의 부분 배열로 나눠야 한다. 아래 그림은 크기가 6×8인 배열을 4개의 그룹으로 나눈 것이고, 1부터 4까지의 수로 나타냈다.

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

5번 연산은 1번 그룹의 부분 배열을 2번 그룹 위치로, 2번을 3번으로, 3번을 4번으로, 4번을 1번으로 이동시키는 연산이다.

3 2 6 3 1 2 9 7 → 2 1 3 8 3 2 6 3
9 7 8 2 1 4 5 3 → 1 3 2 8 9 7 8 2
5 9 2 1 9 6 1 8 → 4 5 1 9 5 9 2 1
2 1 3 8 6 3 9 2 → 6 3 9 2 1 2 9 7
1 3 2 8 7 9 2 1 → 7 9 2 1 1 4 5 3
4 5 1 9 8 2 1 3 → 8 2 1 3 9 6 1 8
     <배열>            <연산 결과>

6번 연산은 1번 그룹의 부분 배열을 4번 그룹 위치로, 4번을 3번으로, 3번을 2번으로, 2번을 1번으로 이동시키는 연산이다.

3 2 6 3 1 2 9 7 → 1 2 9 7 6 3 9 2
9 7 8 2 1 4 5 3 → 1 4 5 3 7 9 2 1
5 9 2 1 9 6 1 8 → 9 6 1 8 8 2 1 3
2 1 3 8 6 3 9 2 → 3 2 6 3 2 1 3 8
1 3 2 8 7 9 2 1 → 9 7 8 2 1 3 2 8
4 5 1 9 8 2 1 3 → 5 9 2 1 4 5 1 9
     <배열>            <연산 결과>

입력

첫째 줄에 배열의 크기 N, M과 수행해야 하는 연산의 수 R이 주어진다.

둘째 줄부터 N개의 줄에 배열 A의 원소 Aij가 주어진다.

마지막 줄에는 수행해야 하는 연산이 주어진다. 연산은 공백으로 구분되어져 있고, 문제에서 설명한 연산 번호이며, 순서대로 적용시켜야 한다.

출력

입력으로 주어진 배열에 R개의 연산을 순서대로 수행한 결과를 출력한다.

제한

  • 2 ≤ N, M ≤ 100
  • 1 ≤ R ≤ 1,000
  • N, M은 짝수
  • 1 ≤ Aij ≤ 108

문제 접근 방법

배열을 돌리는 문제를 전에 프로그래머스에서 "행렬 테두리 회전하기" 라는 문제 이후로 두번째로 풀어본다. 그래서 그런가.. 굉장히... 오래걸렸다.

기본적으로 배열을 입력받은 다음 몇번째 연산인지에 따라 배열을 상하좌우반전 90도회전 그룹이동을 하면되는 문제이다. 따라서 문제에서 요구한대로 단순하게 구현하기만 하면된다.


로직

  1. 상하 좌우반전
    상하의 경우 열 하나씩 선택해서 위와 아래의 요소값을 바꿔주면되고,
    좌우의 경우 행 하나씩 선택해서 왼쪽 오른쪽 요소값을 바꿔주면된다.

  2. 90도 회전
    90도 회전의 경우 가로세로 길이가 다르기 때문에 NxM 배열과 MxN 배열이 필요하다.
    이때 초기 배열을 NxM 배열이라 하면 로테이션된 배열은 MxN 크기를 갖게된다.

    90도를 회전시키는 방법은 간단하게 설명하자면
    회전할 배열의 하나의 열 전체를 떼어다가
    회전한 후 보여질 배열의 하나의 행 전체에 갖다 붙이면된다.

  3. 그룹 이동
    그룹이동도 간단하다.
    그룹이동의 경우 오른쪽 이동만 예를들면
    1번 그룹의 첫번째 요소를 임시변수에 저장해놓고
    1번 그룹의 첫번째 요소자리에는 4번 그룹 첫번째 요소를 넣어준다.
    4번 그룹의 첫번째 요소자리에는 3번 그룹 첫번째 요소를 넣어준다.
    3번 그룹의 첫번째 요소자리에는 2번 그룹 첫번째 요소를 넣어준다.
    마지막 2번 그룹 첫번째 요소자리에는 저장해놨던 임시변수를 넣어주면된다.

    이러한 과정을 첫번째 요소~그룹의 마지막 요소까지 반복하면된다.(왼쪽이동도 같은원리).

  4. 참고
    배열이 90도 회전 한만큼 회전을 수를 카운트해서 어떻게 생겨먹은 배열인지 알필요가 있다.
    (직사각형의 배열이 나올수도 있기때문)

그리고 내가 시간을 많이 잡아 먹은 부분은 90도 회전하는 부분인데
아래 코드에 있는 회전방법이 아니라 다른 방식으로 회전시키려 다가 굉장히 오래 거렸다...
참고했던 회전방법은 아래 링크에 있으니 한번쯤 보면 좋을 것같다.

https://www.youtube.com/watch?v=Z6QwmMQYZr8

 


풀이

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	static int n, m, r, Rcnt;//배열 크기:n,m   연산수:r    회전수:Rcnt 
	static int[][] arr;//초기 배열
	static int[][] Rarr;//회전된 배열
	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());
		r = Integer.parseInt(st.nextToken());
		Rcnt = 2;
		arr = new int[n][m];
		Rarr = new int[m][n];
		
		int[] oper = new int[r];
		
		for(int i=0; i<n; i++) {
			st = new StringTokenizer(br.readLine());
			for(int j=0; j<m; j++) {
				arr[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		
		st = new StringTokenizer(br.readLine());
		for(int i=0; i<r; i++) 
			oper[i] = Integer.parseInt(st.nextToken());
		
		for(int i=0; i<oper.length; i++) {
			int operKind = oper[i];
			if(operKind == 1) reverseUD(); //상하반전
			else if(operKind == 2) reverseLR(); //좌우반전
			else if(operKind == 3) angleR(); //90도회전 오른쪽
			else if(operKind == 4) angleL(); //90회전 왼쪽
			else if(operKind == 5) groupR(); //그룹이동 오른쪽
			else groupL(); //그룹이동 왼쪽
		}
		
		printAll();//연산끝난 배열 출력
	}
	
	public static void reverseUD() {
		if(Rcnt%2==0) {
			for(int i=0; i<m; i++) {
				for(int j=0; j<n/2; j++) {
					int temp = arr[j][i];
					arr[j][i] = arr[n-j-1][i];
					arr[n-j-1][i] = temp;
				}
			}
		}else {
			for(int i=0; i<n; i++) {
				for(int j=0; j<m/2; j++) {
					int temp = Rarr[j][i];
					Rarr[j][i] = Rarr[m-j-1][i];
					Rarr[m-j-1][i] = temp;
				}
			}
		}
		
	}
	
	public static void reverseLR() {
		if(Rcnt%2==0) {
			for(int i=0; i<n; i++) {
				for(int j=0; j<m/2; j++) {
					int temp = arr[i][j];
					arr[i][j] = arr[i][m-j-1];
					arr[i][m-j-1] = temp;
				}
			}
		}else {
			for(int i=0; i<m; i++) {
				for(int j=0; j<n/2; j++) {
					int temp = Rarr[i][j];
					Rarr[i][j] = Rarr[i][n-j-1];
					Rarr[i][n-j-1] = temp;
				}
			}
		}
		
	}
	
	//회전은 한줄씩 떼어다가 회전될 배열에 차례대로 갖다붙이는거임.
	public static void angleR() {
		if(Rcnt%2==0) {
			for(int i=0; i<Rarr.length; i++) {
				for(int j=0; j<Rarr[i].length; j++) {
					Rarr[i][j] = arr[n-j-1][i];
				}
			}
		}else {
			for(int i=0; i<arr.length; i++) {
				for(int j=0; j<arr[i].length; j++) {
					arr[i][j] = Rarr[m-j-1][i];
				}
			}
		}
		Rcnt++;
	}
	
	public static void angleL() {
		if(Rcnt%2==0) {
			for(int i=0; i<Rarr.length; i++) {
				for(int j=0; j<Rarr[i].length; j++) {
					Rarr[i][j] = arr[j][m-i-1];
				}
			}
		}else {
			for(int i=0; i<arr.length; i++) {
				for(int j=0; j<arr[i].length; j++) {
					arr[i][j] = Rarr[j][n-i-1];
				}
			}
		}
		Rcnt++;
	}
	
	//그룹이동은 각그룹에 있는 요소들을 이동될 위치로 하나씩 옮겨주는거임.
	public static void groupR() {
		if(Rcnt%2==0) {
			for(int i=0; i<n/2; i++) {
				for(int j=0; j<m/2; j++) {
					int temp = arr[i][j];
					arr[i][j] = arr[(n/2)+i][j];
					arr[(n/2)+i][j] = arr[(n/2)+i][(m/2)+j];
					arr[(n/2)+i][(m/2)+j] = arr[i][(m/2)+j];
					arr[i][(m/2)+j] = temp;
				}
			}
		}else {
			for(int i=0; i<m/2; i++) {
				for(int j=0; j<n/2; j++) {
					int temp = Rarr[i][j];
					Rarr[i][j] = Rarr[(m/2)+i][j];
					Rarr[(m/2)+i][j] = Rarr[(m/2)+i][(n/2)+j];
					Rarr[(m/2)+i][(n/2)+j] = Rarr[i][(n/2)+j];
					Rarr[i][(n/2)+j] = temp;
				}
			}
		}
		
	}
	public static void groupL() {
		if(Rcnt%2==0) {
			for(int i=0; i<n/2; i++) {
				for(int j=0; j<m/2; j++) {
					int temp = arr[i][j];
					arr[i][j] = arr[i][(m/2)+j];
					arr[i][(m/2)+j] = arr[(n/2)+i][(m/2)+j];
					arr[(n/2)+i][(m/2)+j] = arr[(n/2)+i][j];
					arr[(n/2)+i][j] = temp;
				}
			}
		}else {
			for(int i=0; i<m/2; i++) {
				for(int j=0; j<n/2; j++) {
					int temp = Rarr[i][j];
					Rarr[i][j] = Rarr[i][(n/2)+j];
					Rarr[i][(n/2)+j] = Rarr[(m/2)+i][(n/2)+j];
					Rarr[(m/2)+i][(n/2)+j] = Rarr[(m/2)+i][j];
					Rarr[(m/2)+i][j] = temp;
				}
			}
		
		}
	}
	
	public static void printAll() {
		if(Rcnt%2==0) {
			for(int i=0; i<n; i++) {
				for(int j=0; j<m; j++) {
					System.out.print(arr[i][j]+" ");
				}
				System.out.println();
			}
		}else {
			for(int i=0; i<m; i++) {
				for(int j=0; j<n; j++) {
					System.out.print(Rarr[i][j]+" ");
				}
				System.out.println();
			}
		}
		
	}
}

마치며

이번 문제를 통해 배열을 돌리는 방법들을 좀더 자세히 공부 할수 있었다. 아직 부족한 부분이 많이 보인다. 채우자.