【題目】840. 矩陣中的幻方
3 x 3 的幻方是一個填充有從 1 到 9 的不同數字的 3 x 3 矩陣,其中每行,每列以及兩條對角線上的各數之和都相等。
給定一個由整數組成的 grid,其中有多少個 3 × 3 的 “幻方” 子矩陣?(每個子矩陣都是連續的)。
示例:
輸入: [[4,3,8,4],
[9,5,1,9],
[2,7,6,2]]
輸出: 1
解釋:
下面的子矩陣是一個 3 x 3 的幻方:
438
951
276
而這一個不是:
384
519
762
總的來說,在本示例所給定的矩陣中只有一個 3 x 3 的幻方子矩陣。
提示:
1 <= grid.length <= 10
1 <= grid[0].length <= 10
0 <= grid[i][j] <= 15=
【解題思路1】按定義
可以在代碼中添加 if grid[r+1][c+1] != 5: continue,幫助我們略過一些循環:
- 網格的總和是 45,因爲網格必須是 1 到 9 不同的數字。
- 每一列和行加起來必須是 15,乘以 3 則是網格的總和。
- 對角線的和也必須是 15,題目中說了對角線與列,行的和相同。
- 將四條穿過中心的線的 12 個值相加(即一行一列兩條對角線),這四條線加起來等於 60;而整個網
- 格加起來爲 45。則中心等於 (60-45)/3 = 5
class Solution {
public int numMagicSquaresInside(int[][] grid) {
int R = grid.length, C = grid[0].length;
int ans = 0;
for (int r = 0; r < R-2; ++r)
for (int c = 0; c < C-2; ++c) {
if (grid[r+1][c+1] != 5) continue;
if (magic(grid[r][c], grid[r][c+1], grid[r][c+2],
grid[r+1][c], grid[r+1][c+1], grid[r+1][c+2],
grid[r+2][c], grid[r+2][c+1], grid[r+2][c+2]))
ans++;
}
return ans;
}
public boolean magic(int... vals) {
int[] count = new int[16];
for (int v: vals) count[v]++;
for (int v = 1; v <= 9; ++v)
if (count[v] != 1)
return false;
return (vals[0] + vals[1] + vals[2] == 15 &&
vals[3] + vals[4] + vals[5] == 15 &&
vals[6] + vals[7] + vals[8] == 15 &&
vals[0] + vals[3] + vals[6] == 15 &&
vals[1] + vals[4] + vals[7] == 15 &&
vals[2] + vals[5] + vals[8] == 15 &&
vals[0] + vals[4] + vals[8] == 15 &&
vals[2] + vals[4] + vals[6] == 15);
}
}
【解題思路2】抽屜算法
另一種分析
滿足幻方的條件必定是中間的數字爲5,周圍的數字爲滿足 1->6->7->2->9->4->3->8->1
的這種圓環結構,5的上下左右必定是 1、9、3、7(這四個數字都只能組成兩組 15)
並且5的周圍的數字其實都可以跳過,這裏沒作實現
1.利用數組的索引語義記錄索引對應的下一個數字
2.若5的下一個值滿足條件,且該值的下一行對應列滿足條件,順時針
若5的下一個值滿足條件,且該值的上一行對應列滿足條件,逆時針
3.總共判斷6次即可(前兩次已經判斷過了)
class Solution {
public int numMagicSquaresInside(int[][] grid) {
//if(grid == null || grid.length < 3 || grid[1].length < 3)return 0;
//由於是1~9的不同數字,那麼必定是5在中間,並且其他數字滿足順時針或逆時針
int[] check = {0,6,9,8,3,0,7,2,1,4};
int result = 0;
for(int i=1;i<grid.length-1;i++){
for(int j=1;j<grid[0].length-1;j++){
if(grid[i][j] == 5){
int next = grid[i][j+1];
switch(next){
case 1:
case 3:
case 7:
case 9:
if(check[next] == grid[i+1][j+1]){
if(each(grid,check,i+1,j+1,1))
result++;
}else if(check[next] == grid[i-1][j+1]){
if(each(grid,check,i-1,j+1,-1))
result++;
}
}
//5的後一位必定不用算
j++;
}
}
}
return result;
}
public boolean each(int[][] grid,int[] check,int x,int y,int ward){
int cur = grid[x][y];
int next = check[cur];
for(int i=y-2;y!=i;){
if(grid[x][--y] != next)
return false;
cur = next;
next = check[cur];
}
for(int i=x-2*ward;x!=i;){
if(grid[x-=ward][y] != next)
return false;
cur = next;
next = check[cur];
}
for(int i=y+2;y!=i;){
if(grid[x][++y] != next)
return false;
cur = next;
next = check[cur];
}
return true;
}
}