华为OD机试 - 幻方修复(Java)

题目描述

幻方(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;
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清水乐园

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值