[PAT乙級]1050 螺旋矩陣 (思路+精簡程式碼)
阿新 • • 發佈:2021-01-11
技術標籤:PAT乙級(C語言)
1050 螺旋矩陣
本題要求將給定的 N 個正整數按非遞增的順序,填入“螺旋矩陣”。所謂“螺旋矩陣”,是指從左上角第 1 個格子開始,按順時針螺旋方向填充。要求矩陣的規模為 m 行 n 列,滿足條件:m×n 等於 N;m ≥ n;且 m−n 取所有可能值中的最小值。
輸入格式:
輸入在第 1 行中給出一個正整數 N,第 2 行給出 N 個待填充的正整數。所有數字不超過 104 ,相鄰數字以空格分隔。
輸出格式:
輸出螺旋矩陣。每行 n 個數字,共 m 行。相鄰數字以 1 個空格分隔,行末不得有多餘空格。
輸入樣例:
12
37 76 20 98 76 42 53 95 60 81 58 93
輸出樣例:
98 95 93
42 37 81
53 20 76
58 60 76
思路:經典題目,模擬的思想非常值得學習研究。接收輸入資料和排序不在話下,關鍵在於如何螺旋式地填入資料。
想象自己動手畫這樣一個螺旋矩陣時,應該是先從第一行向右,再從最右側的那一列向下,隨後從底行向左,接著從第一列向上,每次轉向都是因為遇到了邊界或已填入的元素。
那麼,我們也可以嘗試模擬這樣的過程。在計算出行列數後,建立一個四周擴大一圈的二維陣列,將邊界位置的值都設定為1,便於我們設定“牆”,然後從第一行開始向右,直到撞牆,撞牆則轉向向下,持續到撞牆,以此類推,每次撞到牆或者填過的元素(也就是下一格的值不為0),就轉向。
下面給出一種可行的實現方式。
#include <stdio.h>
#include <math.h>
#include <algorithm>
int N, num[10001] = { 0 }, a[10002][102] = { 0 }; //二維陣列開太大會溢位,行最多需要10002,列最多隻需要102(測試點7段錯誤)
int main() {
scanf("%d", &N);
int n = sqrt(N);
while (N % n) n--;
int m = N / n, cnt = N, x = 1, y = 1;
//int a[m + 2][n + 2] = { 0 }; //PAT允許用變數宣告陣列大小,VS不允許,如果這樣做,上邊全域性宣告要註釋掉a[]
for (int i = 0; i < n + 2; i++) a[0][i] = a[m + 1][i] = 1; //填充邊界,下同
for (int j = 0; j < m + 2; j++) a[j][0] = a[j][n + 1] = 1;
for (int i = 0; i < N; i++) scanf("%d", &num[i]);
std::sort(num, num + N); //預設升序排序,所以需要倒序訪問
while (cnt) { //逐次寫入,輪流向右向下向左向上,若不能再移動,直接填入最後一個數
if (a[x - 1][y] && (a[x][y - 1] && !a[x][y + 1])) a[x][y++] = num[--cnt]; //向右,條件是上邊有數且左邊有數、右邊沒有數
//上邊需要有數是防止第四個if向上時第一個if也成立
else if (a[x - 1][y] && !a[x + 1][y]) a[x++][y] = num[--cnt]; //向下,條件是上邊有數且下邊沒有數,其他類比
else if (a[x][y + 1] && !a[x][y - 1]) a[x][y--] = num[--cnt];
else if (a[x + 1][y] && !a[x - 1][y]) a[x--][y] = num[--cnt];
else a[x][y] = num[--cnt]; //四周都已填滿,但是還在while中,說明cnt=1,直接填入最後一個數即可
}
for (int i = 1; i < m + 1; i++)
for (int j = 1; j < n + 1; j++) j == n ? printf("%d\n", a[i][j]) : printf("%d ", a[i][j]);
return 0;
}