洛谷 P1406方格填數 題解
阿新 • • 發佈:2021-12-06
題意
給一個n * n的方格矩陣,還有n * n個整數,讓你將這些整數填入矩陣,使得每行每列每個對角線上整數的和都相等。
輸入格式
第一行一個整數n.(1<=n<=4)
第二行n*n個整數 ai (-10^8 <=ai <= 10^8)
輸出格式
第一行一個整數s 代表每行每列每個對角線的和值
接下來輸出一個n * n的矩陣,表示填數方案。
資料保證有解,可能存在多種方案,輸出字典序最小的(將每行順次相連之後,字典序最小)
樣例輸入
3
1 2 3 4 5 6 7 8 9
樣例輸出
15
2 7 6
9 5 1
4 3 8
題解
首先可以確定,每行、每列、每個對角線的值 sum = 矩陣中所有元素之和除以 n。
因為題目要求按字典序輸出,所以先把陣列從小到大排序
從第一個數進行深度優先搜尋,最後輸出答案,用exit(0)直接結束程式(return ;的時間較長,需要一步一步地往回回溯)。
如何優化? => 可以進行剪枝:在每一行結束、每一列結束或每一對角線結束時可以用一個函式judge判斷這一行、列或對角線之和是否等於 sum。
程式碼
#include <bits/stdc++.h> using namespace std; int n, sum; int b[5][5],v[25], a[25]; void printAns() //輸出答案 { printf("%d\n", sum); for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) printf("%d ", b[i][j]); printf("\n"); } } bool check(int x, int y, int i) { if(y == n) //判斷每一行 { int tsum = a[i]; for(int j = 1; j < n; j++) tsum += b[x][j]; if(tsum != sum) return false; } if(x == n) //判斷每一列 { int tsum = a[i]; for(int j = 1; j < n; j++) tsum += b[j][y]; if(tsum != sum) return false; } if(x == n && y == n) //判斷正對角線 { int tsum = a[i]; for(int j = 1; j < n; j++) tsum += b[j][j]; if(tsum != sum) return false; } if(x == n && y == 1) //判斷負對角線 { int tsum = a[i]; for(int j = 1; j < n; j++) tsum += b[j][n - j + 1]; if(tsum != sum) return false; } return true; } void dfs(int x, int y) { if(x == n + 1) //如果搜到了x+1行,說明已經搜尋完畢,直接輸出。 { printAns(); exit(0); } for(int i = 1; i <= n * n; i++) { if(!v[i]) { if(!check(x, y, i)) continue; //用judge函式進行優化剪枝 v[i] = 1; //標記為已訪問 b[x][y] = a[i]; //將a[i]新增進答案 if(y == n) dfs(x + 1, 1); //如果到達某一行的最後一列,就搜尋下一行 else dfs(x, y + 1); //如果沒有到達某一行的最後一列,就搜尋下一列 v[i] = 0; // 回溯 } } } int main() { scanf("%d", &n); for(int i = 1; i <= n * n; i++) { scanf("%d", &a[i]); sum += a[i]; } sum = sum / n; sort(a + 1, a + 1 + n * n); //因為是字典序小的先輸出,所以要先進行排序 dfs(1,1); return 0; }