题目描述
幻方(Magic Square)是一个由1~N²,共N²个整数构成的N*N矩阵,满足每行、列和对角线上的数字和相等。
上回你已经帮助小明将写错一个数字的幻方进行了修复,小明在感谢之余也想进一步试试你的水平,于是他准备了有两个数字发生了位置交换的幻方。
你可以把这两个交换的数字找出来并且改正吗?
输入描述
第一行输入一个整数N,代表带校验幻方的阶数(3 ≤ N < 50)
接下来的N行,每行N个整数,空格隔开(1 ≤ 每个整数 ≤ N²)
输出描述
输出两行,代表两条纠正信息,注意先输出行号小的,若行号相同则先输出列好小的
每行输出空格隔开的三个整数,分别是:出错行号、出错列号、应填入的数字(末尾无空格)
用例
输入 | 3 8 1 9 3 5 7 4 6 2 |
输出 | 1 3 6 3 2 9 |
说明 | 无 |
Java
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static int n; // 幻方的阶数
static int[][] matrix; // 幻方
static int magic_sum = 0; // 幻和
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
matrix = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = sc.nextInt();
magic_sum += matrix[i][j];
}
}
magic_sum /= n;
getResult();
}
public static void getResult() {
// 记录 和不正确的行
ArrayList<Integer> rows = new ArrayList<>();
// 记录 和不正确的列
ArrayList<Integer> cols = new ArrayList<>();
for (int row = 0; row < n; row++) {
if (getRowSum(row) != magic_sum) {
rows.add(row); // 记录行号
}
}
for (int col = 0; col < n; col++) {
if (getColSum(col) != magic_sum) {
cols.add(col); // 记录列号
}
}
// 两点处于同一行,不同列
if (rows.size() == 0) {
// 则两点的行坐标可能是任意一行
for (int i = 0; i < n; i++) rows.add(i);
}
// 两点处于同一列,不同行
if (cols.size() == 0) {
// 则两点的列坐标可能是任意一列
for (int i = 0; i < n; i++) cols.add(i);
}
// 行号 x 列号,就可以组合出坐标
ArrayList<int[]> positions = new ArrayList<>();
for (Integer row : rows) {
for (Integer col : cols) {
positions.add(new int[] {row, col});
}
}
// 组合两点,尝试交换
for (int i = 0; i < positions.size(); i++) {
for (int j = i + 1; j < positions.size(); j++) {
if (isValid(positions.get(i), positions.get(j))) return;
}
}
}
// 尝试交换两个点,看是否可以复原幻方
public static boolean isValid(int[] pos1, int[] pos2) {
// 获取可能出错的两个点的坐标
int x1 = pos1[0], y1 = pos1[1];
int x2 = pos2[0], y2 = pos2[1];
// 交换可能出错的两个点的值
int tmp = matrix[x1][y1];
matrix[x1][y1] = matrix[x2][y2];
matrix[x2][y2] = tmp;
boolean ans = magic_sum == getDiagonalSum1() && magic_sum == getDiagonalSum2();
// 判断每一行的和是否相等
if (ans)
for (int r = 0; r < n; r++) {
if (getRowSum(r) != magic_sum) {
ans = false;
break;
}
}
// 判断每一列的和是否相等
if (ans)
for (int c = 0; c < n; c++) {
if (getColSum(c) != magic_sum) {
ans = false;
break;
}
}
// 如果所有行、列、对角线都相等,则返回对应交换位置
if (ans) {
if (x1 * n + y1 < x2 * n + y2) {
System.out.println((x1 + 1) + " " + (y1 + 1) + " " + matrix[x1][y1]);
System.out.println((x2 + 1) + " " + (y2 + 1) + " " + matrix[x2][y2]);
} else {
System.out.println((x2 + 1) + " " + (y2 + 1) + " " + matrix[x2][y2]);
System.out.println((x1 + 1) + " " + (y1 + 1) + " " + matrix[x1][y1]);
}
} else {
// 否则复原交换位置
tmp = matrix[x1][y1];
matrix[x1][y1] = matrix[x2][y2];
matrix[x2][y2] = tmp;
}
return ans;
}
// 求对应行的和
public static int getRowSum(int r) {
int sum = 0;
for (int c = 0; c < n; c++) sum += matrix[r][c];
return sum;
}
// 求对应列的和
public static int getColSum(int c) {
int sum = 0;
for (int r = 0; r < n; r++) sum += matrix[r][c];
return sum;
}
// 求对角线的和(左上,到右下)
public static int getDiagonalSum1() {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += matrix[i][i];
}
return sum;
}
// 求对角线的和(右上,到左下)
public static int getDiagonalSum2() {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += matrix[i][n - 1 - i];
}
return sum;
}
}