yukicoder No.1169 Row and Column and Diagonal

構築系の問題。
Writer解と違ったので、1つのアイディアとして書いてみます。

問題

解法
  • (i, i)のマスから斜め左下に向かってiで埋めていく。
  • 左端(x, 1)に達したら、次は右端の1つ下のマス(x+1, N)
    下端(N, y)に達したら、次は上端の1つ左のマス(1, y-1)
具体例

N=5(サンプル1)の場合、以下のようになる。

1 4 2 5 3
4 2 5 3 1
2 5 3 1 4
5 3 1 4 2
3 1 4 2 5
証明のようなもの
  • 「1」で埋めるマスについて考えると、(1, 1)(2, N)(3, N-1)→・・・と埋めていくことになり、左端で折り返した後に埋めるマスが(x, N-x+2)となる。
  • Nが奇数という前提の下では、x=\lceil N/2 \rceilとすると、(\lceil N/2 \rceil, \lfloor N/2 \rfloor +2) = (\lceil N/2 \rceil, \lceil N/2 \rceil +1)となるため、(j, j)(j \neq i)を通らない。つまり、他の値で埋めるマスと重複しない。
    ※初期位置(i, i)以外で左上から右下の対角線と交わるのは1回だけ。
  • 他の値についても、下端で折り返す場合についても、同様に(j, j+1)を通ることで(j, j)を通らないことが言えるはず。
  • 各行と各列が1, \ldots, Nの順列であることは、埋め方から自明であると思う。

なお、Nが偶数の場合は、必ず(j, j)(j \neq i)を通るため、この方法では絶対に構築できない。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.close();

        int[][] a = new int[n][n];
        for (int i = 1; i <= n; i++) {
            int x = i - 1;
            int y = i - 1 + n;
            for (int j = 0; j < n; j++) {
                a[x % n][y % n] = i;
                x++;
                y--;
            }
        }
        for (int i = 0; i < n; i++) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < n; j++) {
                sb.append(a[i][j]).append(' ');
            }
            sb.deleteCharAt(sb.length() - 1);
            System.out.println(sb.toString());
        }
    }
}