백준 17406 - 배열 돌리기 4
Updated:
Java
17406 번 - 배열 돌리기 4
문제
크기가 N×M 크기인 배열 A가 있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다.
배열 A와 사용 가능한 회전 연산이 주어졌을 때, 배열 A의 값의 최솟값을 구해보자. 회전 연산은 모두 한 번씩 사용해야 하며, 순서는 임의로 정해도 된다.
접근 방법
배열 돌리기 1,2,3을 해결하였으면 쉽게 풀 수 있는 문제였다.
주어진 문제는 k개의 회전 연산
을 섞어가며 수행한 뒤, 변환 된 배열의 최소 값을 구하는 것이다.
위 문제를 해결하기 위해 코드의 구성은 다음과 같다.
- k개의
회전 연산
을 순열을 통해 섞는다. - 섞은
회전 연산
k개를 수행하여 배열을 변환한다. - 변환한 배열 n개의 행들의 값 합을 구하여, 그 중 최솟값을 구한다.
코드
import java.util.*;
import java.io.*;
public class Main {
static int n, m, k, result = 987654321, r, c, s, size;
static int[] sel, vsi;
static int[][] arr, oper, saved, nArr;
public static void main(String []args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine());
n = stoi(stk.nextToken());
m = stoi(stk.nextToken());
k = stoi(stk.nextToken());
arr = new int[n][m]; // 값 배열
saved = new int[n][m]; // 백업 내용
oper = new int[k][3]; // 연산
// A 배열과, 백업 용 배열을 초기화 한다.
for(int i = 0; i < n; i++) {
stk = new StringTokenizer(br.readLine());
for(int j = 0; j < m; j++) {
arr[i][j] = stoi(stk.nextToken());
saved[i][j] = arr[i][j];
}
}
// 회전 연산의 정보를 저장한다.
for(int i = 0; i < k; i++) {
stk = new StringTokenizer(br.readLine());
for(int j = 0; j < 3; j++) {
oper[i][j] = stoi(stk.nextToken());
}
}
// 회전 연산 순서에 대한 순열을 제작할 대 필요한 배열 선언
sel = new int[k];
vsi = new int[k];
// 순열을 제작하고, 그에 따른 회전 연산을 수행한다.
DFS(0);
System.out.println(result);
br.close();
}
// K! 만큼 반복을 하여 회전 연산 순서 순열을 만든다.
static void DFS(int lv) {
if(lv == k) {
// A 배열을 초기 상태로 복구한다.
for(int i = 0; i < n; i++) {
arr[i] = saved[i].clone();
}
Cal(); // 회전 연산
int sum = 0;
// 회전 연산 후 배열의 값 구하기
for(int[] vv : arr) {
sum = 0;
for(int v : vv) {
sum += v;
}
// 최솟 값 저장한다.
result = Math.min(sum, result);
}
return;
}
for(int i = 0; i < k; i++) {
if(vsi[i] == 0) {
vsi[i] = 1;
sel[lv] = i;
DFS(lv + 1);
vsi[i] = 0;
}
}
}
static int i, j, d = 0;
static int dy[] = {1,0,-1,0}; // 하, 우, 상, 좌
static int dx[] = {0,1,0,-1};
static void Cal() {
for(int lv = 0; lv < k; lv++) {
r = oper[sel[lv]][0];
c = oper[sel[lv]][1];
s = oper[sel[lv]][2];
size = 2*s+1;
nArr = new int[size][size];
// 돌릴 2차원 배열 크기 만큼 arr에서 떼어온다.
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
nArr[i][j] = arr[r - s - 1 + i][c - s - 1 + j];
}
}
// 떼어온 2차원 배열을 시계방향으로 한칸 이동한다
for(int idx = 0; idx < s; idx++) {
i = idx;
j = idx;
d = 0;
int tmp = nArr[i][j];
while(d != 4) {
// 만약 범위를 벗어나면, 대입하는 값의 방향을 바꾼다.
if(i + dy[d] < idx || i + dy[d] >= size - idx || j + dx[d] < idx || j + dx[d] >= size - idx) {
d++;
continue;
}
nArr[i][j] = nArr[i + dy[d]][j + dx[d]];
i += dy[d];
j += dx[d];
}
nArr[idx][idx + 1] = tmp;
}
// 떼어온 2차원 배열을 다시 원본에 붙힌다.
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
arr[r - s - 1 + i][c - s - 1 + j] = nArr[i][j];
}
}
}
}
static int stoi(String str) {
return Integer.parseInt(str);
}
}
총평
난이도
⭐⭐★★★
후기
문제 조건인 행/열 최소 값은 2의 배수이다를 읽지 않아, 쓸데없이 고생했다.
문제를 잘 읽어라는 교수님 말씀이 뭔지 새삼 깨닫게된다 정말로,,
개선할 점
문제 좀 읽자!!!!