3 x 3 메트릭스의 입력을 받아 3 x 3 마방진 값 목록과 비교하여 각 마방진과 비교 후에 각 값의 절대값 차이가 제일 작은 값을 구하여라.
We define a magic square to be an n X n matrix of distinct positive integers from 1 to n2 where the sum of any row, column, or diagonal of length n is always equal to the same number: the magic constant.
You will be given a 3 X 3 matrix s of integers in the inclusive range [1,9]. We can convert any digit a to any other digit b in the range [1,9] at cost of |a - b|. Given s, convert it into a magic square at minimal cost. Print this cost on a new line.
Note: The resulting magic square must contain distinct integers in the inclusive range [1,9].
For example, we start with the following matrix s:
1 5 3 4
2 1 5 8
3 6 4 2
We can convert it to the following magic square:
1 8 3 4
2 1 5 9
3 6 7 2
This took three replacements at a cost of |5 - 8| + |8 - 9| + |4 - 7| = 7.
Complete the formingMagicSquare function in the editor below. It should return an integer that represents the minimal total cost of converting the input square to a magic square.
formingMagicSquare has the following parameter(s):
s: a 3 X 3 array of integers
Each of the lines contains three space-separated integers of row s[i].
Print an integer denoting the minimum cost of turning matrix s into a magic square.
1 4 9 2
2 3 5 7
3 8 1 5
1 1
If we change the bottom right value, s[2][2], from 5 to 6 at a cost of |6 - 5| = 1, s becomes a magic square at the minimum possible cost.
1 4 8 2
2 4 5 7
3 6 1 6
1 4
Using 0-based indexing, if we make
s[0][1]->9 at a cost of |9 - 8| = 1
s[1][0]->3 at a cost of |3 - 4| = 1
s[2][0]->8 at a cost of |8 - 6| = 2,
then the total cost will be 1 + 1 + 2 = 4.
처음에 이 문제를 시작할때 마방진이라는 개념 자체에 어려움이 있었다.(졸업한지가 꽤 되어서 기억조차..) 마방진이 무엇인지 먼저 학습을 하기 위해서 다음의 유튜브 영상을 확인 한 후 마방진 자체에 대한 이해를 시도
마방진 또는 방진은 n²개의 수를 가로, 세로, 대각선 방향의 수를 더하면 모두 같은 값이 나오도록 n × n 행렬에 배열한 것이다. 마법진 중 하나이다. 일반적인 마방진의 각 칸에는 1부터 n²까지의 수가 한 개씩 들어간다. 마방진은 n이 2일 때를 제외하고 항상 존재한다. (위키백과)
1 5 9
1 6 8
2 6 7
2 8 5
2 9 4
3 8 4
3 7 5
4 5 6
2번 참조 : 1, 3, 7, 9
3번 참조 : 2, 4, 6, 8
4번 참조 : 5
마방진에 대한 비밀을 알았는데 이 원리를 알면 해결이 될 것인가 고민을 했는데 1시간을 고민해도 해결이 안나왔다가 이런 생각이 들었다. 가운데가 5로 고정된 1-9사이의 정수의 조합을 구해서 가로/세로/대각선의 합이 15인 케이스를 구한뒤 입력 값만 구하면 되지 않을까? 라는 생각이 들며 그럼 어떻게 "가운데가 5로 고정된 1-9사이의 정수의 조합"을 구해 볼까 고민을 하게 되었고 DFS를 이용한 구하는 것이 맞다고 생각했다. 그래서 문제를 다음과 같이 풀었다.
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
class Result {
public final static int standard = 15;
/*
* 쓰이지 않음
*/
public static int[][] inputListToArray(List<List<Integer>> s) {
int array[][] = new int[3][3];
for( int i = 0; i < 3 ; i++ ) {
for( int j = 0; j < 3 ; j++ ) {
array[i][j] = s.get(i).get(j);
}
}
return array;
}
/**
* 가운데가 5로 고정된 1-9사이의 정수의 조합을 구한다.(DFS이용)
*/
public static List<List<Integer>> generateToMagicSquare(
List<List<Integer>> candidates,
int[] target,
int pivot
) {
if(pivot == target.length) {
List<Integer> temp = new ArrayList<>();
for( int i = 0; i < target.length ; i++ ) {
temp.add(target[i]);
}
temp.add(4, 5);
candidates.add(temp);
} else {
for( int i = pivot ; i < target.length ; i++ ) {
swap(target, i, pivot);
generateToMagicSquare(candidates, target, pivot + 1);
swap(target, pivot, i);
}
}
return candidates;
}
public static void swap(int array[], int before, int after) {
int temp = array[before];
array[before] = array[after];
array[after] = temp;
}
/**
* 2. 1의 결과 중에 합이 15인 것만 남긴다.
*/
public static List<List<Integer>> filteredMagicSquare(
List<List<Integer>> input) {
return input.stream()
.filter(it -> standard == (it.get(0) + it.get(1) + it.get(2)))
.filter(it -> standard == (it.get(3) + it.get(4) + it.get(5)))
.filter(it -> standard == (it.get(6) + it.get(7) + it.get(8)))
.filter(it -> standard == (it.get(0) + it.get(3) + it.get(6)))
.filter(it -> standard == (it.get(1) + it.get(4) + it.get(7)))
.filter(it -> standard == (it.get(2) + it.get(5) + it.get(8)))
.filter(it -> standard == (it.get(0) + it.get(4) + it.get(8)))
.filter(it -> standard == (it.get(2) + it.get(4) + it.get(6)))
.collect(Collectors.toList());
}
public static int formingMagicSquare(List<List<Integer>> s) {
List<List<Integer>> candidates = new ArrayList<>();
int[] target = {1, 2, 3, 4, 6, 7, 8, 9};
List<List<Integer>> magicSquareCandidates = generateToMagicSquare(
candidates,
target,
0
);
magicSquareCandidates = filteredMagicSquare(magicSquareCandidates);
// 3. 입력값으로 구한 메트릭스와 마방진 메트릭스 후보와 비교해서 값을 구한다.
int answer = Integer.MAX_VALUE;
for(List<Integer> item : magicSquareCandidates) {
int sum = 0;
for( int i = 0 ; i < 3 ; i++ ) {
for( int j = 0 ; j < 3 ; j++ ) {
sum += Math.abs(s.get(i).get(j) - item.get((i * 3) + j));
}
}
if(answer > sum) {
answer = sum;
}
}
return answer;
}
}
public class Solution {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
List<List<Integer>> s = new ArrayList<>();
IntStream.range(0, 3).forEach(i -> {
try {
s.add(
Stream.of(bufferedReader.readLine().replaceAll("\\s+$", "").split(" "))
.map(Integer::parseInt)
.collect(toList())
);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
int result = Result.formingMagicSquare(s);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedReader.close();
bufferedWriter.close();
}
}