幻方正中心一定是 5
$3\times 3$ 的幻方必须包含 $1$ 到 $9$ 所有数字,这意味着幻方元素和为 $1+2+\cdots + 9 = 45$。
由于每行的和都要相等,所以每行的和为 $\dfrac{45}{3} = 15$。这也是每列的和,对角线的和。
有 $4$ 条经过幻方正中心的线:第二行,第二列,两条对角线。
这 $4$ 条线:
- 覆盖了 $3\times 3$ 幻方的每个数,正中心的数覆盖了 $4$ 次(多覆盖了 $3$ 次),其余数恰好覆盖一次。
- 总和为 $15\cdot 4 = 60$。
设正中心的数为 $x$ ,则有
$$
1+2+\cdots + 9 + 3x = 60
$$
解得
$$
x = 5
$$
换言之(逆否命题),如果 $3\times 3$ 矩阵正中心的数不是 $5$,那么该矩阵必然不是幻方。
无需计算第三行的和,第三列的和
如果 $3\times 3$ 矩阵包含 $1$ 到 $9$ 所有整数,且前两行的和都是 $15$,那么第三行的和为
$$
1+2+\cdots+9-15-15 = 15
$$
同理,如果前两列的和都是 $15$,那么第三列的和必然是 $15$。
无需计算对角线的和
如果 $3\times 3$ 矩阵:
- 正中心的数是 $5$。
- 包含 $1$ 到 $9$ 所有整数。
- 前两行的和都是 $15$。
- 前两列的和都是 $15$。
下面证明:矩阵对角线的和一定都是 $15$。
设矩阵为
$$
\begin{bmatrix}
a & b & c \
d & 5 & f \
g & h & i \
\end{bmatrix}
$$
由于正中心的数是 $5$,只需证明对角两数之和等于 $10$,即 $a+i=c+g=10$。
在 $1$ 到 $9$ 中选两个不同整数相加等于 $10$,只有如下 $4$ 种情况:
- $1+9=10$。
- $2+8=10$。
- $3+7=10$。
- $4+6=10$。
由于已知 $b+h=d+f=10$,消耗了两种情况,剩余的四个数必然在 $a,c,g,i$ 中。比如 $b+h=1+9$,$d+f=3+7$,那么剩余的 $2,4,6,8$ 必然在 $a,c,g,i$ 中。
比如左上角的 $a=4$,那么 $6$ 在哪?
- 在右上角?此时 $a+c=10$,由于已知 $a+b+c=15$,得到 $b=5$,与正中心的数相同,矛盾。
- 在左下角?此时 $a+g=10$,由于已知 $a+d+g=15$,得到 $d=5$,与正中心的数相同,矛盾。
- 所以 $6$ 只能在右下角,即 $a+i=10$ 成立。
- 剩余两个数 $2$ 和 $8$ 在另一对角,所以 $c+g=10$ 成立。
用同样的方法可以证明,$a$ 等于其他数的时候,对角的数 $i$ 一定等于 $10-a$。剩余的另一对和为 $10$ 的数在另一对角。
综上,由已知条件可得,两条对角线的和都是 $15$。
如何快速判断矩阵包含 $1$ 到 $9$ 所有数?可以把数字压缩到一个二进制数 $\textit{mask}$ 中,$\textit{mask}$ 从低到高的 $i$ 位是 $1$ 表示 $i$ 在矩阵中。矩阵包含 $1$ 到 $9$ 所有数相当于 $\textit{mask} = 1111111110_{(2)} = 2^{10}-2=1022$。
###py
class Solution:
def numMagicSquaresInside(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
ans = 0
for i in range(m - 2):
for j in range(n - 2):
if grid[i + 1][j + 1] != 5:
continue
mask = 0
r_sum = [0] * 3
c_sum = [0] * 3
for r in range(3):
for c in range(3):
x = grid[i + r][j + c]
mask |= 1 << x
r_sum[r] += x
c_sum[c] += x
if mask == 1022 and r_sum[0] == r_sum[1] == c_sum[0] == c_sum[1] == 15:
ans += 1
return ans
###java
class Solution {
public int numMagicSquaresInside(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int ans = 0;
for (int i = 0; i < m - 2; i++) {
for (int j = 0; j < n - 2; j++) {
if (grid[i + 1][j + 1] != 5) {
continue;
}
int mask = 0;
int[] rSum = new int[3];
int[] cSum = new int[3];
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
int x = grid[i + r][j + c];
mask |= 1 << x;
rSum[r] += x;
cSum[c] += x;
}
}
if (mask == (1 << 10) - 2 &&
rSum[0] == 15 && rSum[1] == 15 &&
cSum[0] == 15 && cSum[1] == 15) {
ans++;
}
}
}
return ans;
}
}
###cpp
class Solution {
public:
int numMagicSquaresInside(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
int ans = 0;
for (int i = 0; i < m - 2; i++) {
for (int j = 0; j < n - 2; j++) {
if (grid[i + 1][j + 1] != 5) {
continue;
}
int mask = 0;
int r_sum[3]{};
int c_sum[3]{};
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
int x = grid[i + r][j + c];
mask |= 1 << x;
r_sum[r] += x;
c_sum[c] += x;
}
}
if (mask == (1 << 10) - 2 &&
r_sum[0] == 15 && r_sum[1] == 15 &&
c_sum[0] == 15 && c_sum[1] == 15) {
ans++;
}
}
}
return ans;
}
};
###c
int numMagicSquaresInside(int** grid, int gridSize, int* gridColSize) {
int m = gridSize, n = gridColSize[0];
int ans = 0;
for (int i = 0; i < m - 2; i++) {
for (int j = 0; j < n - 2; j++) {
if (grid[i + 1][j + 1] != 5) {
continue;
}
int mask = 0;
int r_sum[3] = {};
int c_sum[3] = {};
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
int x = grid[i + r][j + c];
mask |= 1 << x;
r_sum[r] += x;
c_sum[c] += x;
}
}
if (mask == (1 << 10) - 2 &&
r_sum[0] == 15 && r_sum[1] == 15 &&
c_sum[0] == 15 && c_sum[1] == 15) {
ans++;
}
}
}
return ans;
}
###go
func numMagicSquaresInside(grid [][]int) (ans int) {
m, n := len(grid), len(grid[0])
for i := range m - 2 {
for j := range n - 2 {
if grid[i+1][j+1] != 5 {
continue
}
mask := 0
var rSum, cSum [3]int
for r, row := range grid[i : i+3] {
for c, x := range row[j : j+3] {
mask |= 1 << x
rSum[r] += x
cSum[c] += x
}
}
if mask == 1<<10-2 &&
rSum[0] == 15 && rSum[1] == 15 &&
cSum[0] == 15 && cSum[1] == 15 {
ans++
}
}
}
return
}
###js
var numMagicSquaresInside = function (grid) {
const m = grid.length;
const n = grid[0].length;
let ans = 0;
for (let i = 0; i < m - 2; i++) {
for (let j = 0; j < n - 2; j++) {
if (grid[i + 1][j + 1] !== 5) {
continue;
}
let mask = 0;
const rSum = [0, 0, 0];
const cSum = [0, 0, 0];
for (let r = 0; r < 3; r++) {
for (let c = 0; c < 3; c++) {
const x = grid[i + r][j + c];
mask |= 1 << x;
rSum[r] += x;
cSum[c] += x;
}
}
if (mask === (1 << 10) - 2 &&
rSum[0] === 15 && rSum[1] === 15 &&
cSum[0] === 15 && cSum[1] === 15) {
ans++;
}
}
}
return ans;
};
###rust
impl Solution {
pub fn num_magic_squares_inside(grid: Vec<Vec<i32>>) -> i32 {
let m = grid.len();
let n = grid[0].len();
let mut ans = 0;
for i in 0..m.saturating_sub(2) {
for j in 0..n.saturating_sub(2) {
if grid[i + 1][j + 1] != 5 {
continue;
}
let mut mask = 0;
let mut r_sum = [0; 3];
let mut c_sum = [0; 3];
for (r, row) in grid[i..i + 3].iter().enumerate() {
for (c, &x) in row[j..j + 3].iter().enumerate() {
mask |= 1 << x;
r_sum[r] += x;
c_sum[c] += x;
}
}
if mask == (1 << 10) - 2 &&
r_sum[0] == 15 && r_sum[1] == 15 &&
c_sum[0] == 15 && c_sum[1] == 15 {
ans += 1;
}
}
}
ans
}
}
复杂度分析
- 时间复杂度:$\mathcal{O}(mn)$,其中 $m$ 和 $n$ 分别是 $\textit{grid}$ 的行数和列数。
- 空间复杂度:$\mathcal{O}(1)$。
分类题单
如何科学刷题?
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
我的题解精选(已分类)
欢迎关注 B站@灵茶山艾府