本題要求將給定的 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
本題是要按螺旋的順序來爲一個空二維矩陣的每個位置填充數字,與這種規律類似的還有Z字型填充,就像下圖這樣:
這種題我總結了一種通用做法,就是假設有一個指針在指引着填充數字的位置,然後找出這個指針的所有運動方向。在本題中這個指針有四個運動方向,即上下左右,不同狀態下矩陣的兩個下標的變化不同。假設這個二維矩陣的兩個下標爲i和j。則分析螺旋的四種狀態下的變化:
上:在同一列運動,j不變,i減1。若走到頭則轉爲右
下:在同一列運動,j不變,i加1。若走到頭則轉爲左
左:在同一行運動,i不變,j減1。若走到頭則轉爲下
右:在同一行運動,i不變,j加1。若走到頭則轉爲上
走到頭包括兩種情況,一種是到了矩陣的邊界,再前進則越界,另一種是下一個位置已經有數字了。走到頭後就應該變換前進方向。找出運動的規律之後依次填入數字,代碼如下:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int N;
cin>>N;
int num[N];
for(int i=0;i<N;i++)
scanf("%d",&num[i]);
sort(num,num+N);
int n=(int)sqrt(N),m;//計算矩陣的行和列
while(N%n!=0)
n--;
m=N/n;
int res[m][n],i=0,j=0,flag=1;//i和j代表下標,flag表示前進方向
memset(res,0,sizeof(res));
for(int k=N-1;k>=0;k--)//要以非遞增順序填充,則排序後倒着遍歷
{
if(flag==1)//向右
{
res[i][j]=num[k];
j++;
if(j==n || res[i][j]!=0)//走到頭要轉爲向下,因爲此時的下標是越界的,所以要糾正
{
j--;
i++;
flag=2;
}
}
else if(flag==2)//向下
{
res[i][j]=num[k];
i++;
if(i==m || res[i][j]!=0)//轉爲向左,並糾正座標
{
j--;
i--;
flag=3;
}
}
else if(flag==3)//向左
{
res[i][j]=num[k];
j--;
if(j==-1 || res[i][j]!=0)//轉爲向上,糾正座標
{
j++;
i--;
flag=4;
}
}
else//向上
{
res[i][j]=num[k];
i--;
if(i==-1 || res[i][j]!=0)//轉爲向右,糾正座標
{
i++;
j++;
flag=1;
}
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cout<<res[i][j];
if(j!=n-1)
cout<<" ";
}
cout<<endl;
}
return 0;
}