- C++
二维数组图形规律
- @ 2026-8-21 14:08:40
点击这里查看或者收起讲义:【二维数组图形规律】
二维数组图形规律
一、二维数组的遍历规律
1、一维数组遍历
我们从一维数组的遍历拓展到二维数组的遍历

int n=10,k=0,a[10];
for(int i=0;i<n;i++){//顺序遍历填数
a[i]=k++;
}
for(int i=n-1;i>0;i--){//倒序遍历输出
cout<<a[i]<<" ";
}
2、二维数组遍历
由二维数组的定义可知,二维数组相当于一维数组里面嵌套了一个一维数组(如下图),所以二维数组的遍历相当于在一维数组的 循环里面再嵌套一个 循环。

(本文的讨论分析,均假设二维数组不包含第 行,第 列,包含的情况,请大家自行分析)
//不考虑第0行,第0列
//图1 外循环为行i,内循环为每列j
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[10][10],n=4,m=4,k=0;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
a[i][j]= ++k;//遍历顺序
}
}
for(int i=1; i<=n; i++) {//输出
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码输出结果为:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
思考如下图形的遍历顺序:

//不考虑第0行,第0列
//以下代码仅修改图1代码的第7-11行
//图2 外循环i是倒序,内循环j也是倒序
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
a[i][j]= ++k;
}
}
//图3 外循环j是顺序,内循环i也是顺序
for(int j=1;j<=m;j++){
for(int i=1;i<=n;i++){
a[i][j]= ++k;
}
}
//图4 外循环j是倒序,内循环i是顺序
for(int j=m;j>=1;j--){
for(int i=1;i<=n;i++){
a[i][j]= ++k;
}
}
3、二维数组遍历进阶
斜向顺序遍历1:
#include <bits/stdc++.h>
using namespace std;
int i,j,n,a[20][20] = {0},t=0;
int main(){
cin>>n;
for(i = 1;i<=n;i++){
for(j = 1;j <=i;j++){
a[j][i-j+1] = ++t;//观察第三行(1,3) (2,2) (3,1) i=3 j=1 2 3
}
}
//输出
for(i = 1;i <= n;i++){
for(j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码输出结果为:
1 2 4 7 11
3 5 8 12 0
6 9 13 0 0
10 14 0 0 0
15 0 0 0 0
斜向顺序遍历2:
#include <bits/stdc++.h>
using namespace std;
int i,j,n,m,a[20][20] = {0},t=0;
int main(){
cin>>n;
for(i = 1;i<=2*n-1;i++){//斜向的行数i
if(i<=n)m=i; //左上角
else m=n-i%n; //右下角
for(j = 1;j <=m;j++){//斜向的列数j m=1,2...n...2,1;
if(i<=n)a[j][i-j+1] = ++t;//填写左上角
else a[i%n+j][n-j+1]=++t; //填写右下角
}
}
//输出
for(i = 1;i <= n;i++){
for(j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码输出结果为:
1 2 4 7 11
3 5 8 12 16 //i=6 j=1 2 3 4 (2,5) (3,4) (4,3) (5,2)
6 9 13 17 20 //i=7 j=1 2 3 (3,5) (4,4) (5,3)
10 14 18 21 23
15 19 22 24 25
可以分开左上角和右下角两部分写:
#include <bits/stdc++.h>
using namespace std;
int i,j,n,a[20][20] = {0},b[20][20],t=0;
int main(){
cin>>n;
for(i = 1;i<=n;i++){
for(j = 1;j <=i;j++){
a[i+1-j][j] = ++t;
b[j][i+1-j]=t;
}
}
for(int i=1;i<n;i++){
for(int j=n-i;j>=1;j--){
a[i+j][n+1-j] = ++t;
b[n+1-j][i+j]=t;
}
}
//输出
for(i = 1;i <= n;i++){
for(j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
cout<<endl<<endl;
for(i = 1;i <= n;i++){
for(j = 1;j <= n;j++){
cout<<setw(3)<<b[i][j];
}
cout<<endl;
}
return 0;
}
可以利用对角线对称原理求
#include <bits/stdc++.h>
using namespace std;
int i,j,n,a[20][20] = {0},b[20][20],t=0;
int main(){
cin>>n;
for(i = 1;i<=n;i++){
for(j = 1;j <=i;j++){
a[i+1-j][j] = ++t;
//a[n-(j)+1 ][n- (i+1-j)+1 ]= n*n - i*(i+1)/2 + j;
if(i!=n)a[n-(j)+1 ][n- (i+1-j)+1 ]= n*n+1-i+j-t;
}
}
//输出
for(i = 1;i <= n;i++){
for(j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
cout<<endl<<endl;
return 0;
}
找出以下矩阵的规律,思考如何得到以下矩阵:
1 3 6 10 15 11 7 4 2 1 25 24 22 19 15 15 19 22 24 25
2 5 9 14 19 16 12 8 5 3 23 21 18 14 10 10 14 18 21 23
4 8 13 18 22 20 17 13 9 6 20 17 13 9 6 6 9 13 17 20
7 12 17 21 24 23 21 18 14 10 16 12 8 5 3 3 5 8 12 16
11 16 20 23 25 25 24 22 19 15 11 7 4 2 1 1 2 4 7 11
斜向顺序遍历3:
for(i = 1;i<=n;i++){
for(j = 1;j <= n - i+1;j++){
a[i+j-1][j] = t;//左下角部分
a[j][i+j-1] = t;//右下角部分
t++;
}
}
上述代码输出结果为:
1 6 10 13 15
6 2 7 11 14
10 7 3 8 12
13 11 8 4 9
15 14 12 9 5
利用下文数组对称规则,可以简单修改第9和第10行代码即可得到各种对称翻转后的图形。
15 13 10 6 1 15 14 12 9 5 5 9 12 14 15
14 11 7 2 6 13 11 8 4 9 9 4 8 11 13
12 8 3 7 10 10 7 3 8 12 12 8 3 7 10
9 4 8 11 13 6 2 7 11 14 14 11 7 2 6
5 9 12 14 15 1 6 10 13 15 15 13 10 6 1
【BCOI】 习题:
二、二维数组对角线规律
数组,,第 行,第 列为 ,且先不考虑第行,第列的情况。

观察上图可知,二维数组左斜对角线上格子的的值都相等,右斜对角线上格子的的值都相等。
下面我们分左斜和右斜两种情况讨论。
1、左斜对角线

每条对角线上的均具有 值规律,即每条对角线上的格子的(或)的值均相等。
值可以根据题意 ,将上述k值序列,进行数位移位从而得到新的序列值(见上图)。
根据值规律,我们可以把二维数组分为上下两部分:
int k=j-i;
if(k==0)为红色对角线上的格子
if(k<0)为左下角绿色部分格子
if(k>0)为右上角蓝色部分格子
根据对角线规则,就可以很简单的得到符合对角线规则的二维矩阵:
左斜矩阵一:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[6][6],n=5;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
a[i][j]=j-i;//k=j-i;
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码将数组赋值为:
0 1 2 3 4
-1 0 1 2 3
-2 -1 0 1 2
-3 -2 -1 0 1
-4 -3 -2 -1 0
左斜矩阵二:
int a[6][6],n=5;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=n-(j-i);//k=n-(j-i);
}
}
上述代码将数组赋值为:
5 4 3 2 1
6 5 4 3 2
7 6 5 4 3
8 7 6 5 4
9 8 7 6 5
左斜矩阵三:
int a[6][6],n=5;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=n-(i-j);//k=n-(i-j);
}
}
上述代码将数组赋值为:
5 6 7 8 9
4 5 6 7 8
3 4 5 6 7
2 3 4 5 6
1 2 3 4 5
左斜矩阵四:
如果取上述矩阵二的右上角和矩阵三的左下角部分,得到新的矩阵四:
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(j-i>=0)
a[i][j]=n-(j-i);//对角线右上角
else
a[i][j]=n-(i-j);//对角线左下角
}
}
上述代码将数组赋值为:
5 4 3 2 1
4 5 4 3 2
3 4 5 4 3
2 3 4 5 4
1 2 3 4 5
上述矩阵四也可使用以下更加简洁的代码赋值得到:
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
a[i][j]=n-abs(i-j);
}
}
2、右斜对角线

每条对角线上的均具有 值规律,即每条对角线上的格子的 的值均相等。
值可以根据题意 ,将上述k值序列,进行数位移位从而得到新的序列值(见上图)。
同样根据 值规律,我们可以把二维数组分为上下两部分:
int k=i+j-1;
if(k==n)为红色对角线上的格子
if(k<n)为左上角橙色部分格子
if(k>n)为右下角紫色部分格子
根据对角线规则,就可以很简单的得到符合对角线规则的二维矩阵:
右斜矩阵五:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[6][6],n=5;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
a[i][j]=i+j-1;//k=i+j-1;
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码将数组赋值为:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
右斜矩阵六:
int a[6][6],n=5;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=2*n-(i+j-1);
}
}
上述代码将数组赋值为:
9 8 7 6 5
8 7 6 5 4
7 6 5 4 3
6 5 4 3 2
5 4 3 2 1
右斜矩阵七:
如果取上述矩阵一的左上角和矩阵二的右下角部分,得到新的矩阵三:
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
if(i+j-1<n)
a[i][j]=i+j-1;//对角线左上角
else
a[i][j]=2*n-(i+j-1);//对角线右下角
}
}
上述代码将数组赋值为:
1 2 3 4 5
2 3 4 5 4
3 4 5 4 3
4 5 4 3 2
5 4 3 2 1
包含第0行和第0列数组的对角线规则

参考例题:
三、数组对称规则
**数组对称规则非常实用,解题时注意观察图形规律,然后利用对称规则可以让问题简化很多,例如很多时候只需要考虑一半或者四分之一的部分答案,剩下部分直接利用对称原理赋值即可。**本章的方阵填数,大部分都可以用这个规则来优化。
1、一维数组对称规则

int a[10],n=5;
for(int i=1;i<=n/2;i++){//循环一半
a[i]=a[n-i+1]=i;//头尾对称赋值
}
上述代码将数组赋值为:
1 2 3 2 1
可以利用一维数组对称规则,判定头尾是否相等,从而验证数列或者字符串为回文数:
char c[105]; bool flag=0;
cin>>c+1;//表示字符串从c[1]开始存储,跳过c[0]位置;
int n=strlen(c);
for(int i=1;i<=n/2;i++){
if(c[i]!=c[n-i+1]){
flag=1;break;
}
}
if(!flag)c是回文串;
else c不是回文串
根据上述对称规则可知:
:表示把 倒序一下 :表示把 倒序一下
上述规则在下面会经常用到。
2、二维数组对称规则
a) 关于 左右对称

二维数组关于 左右对称,则行 不变, 方向成一维数组对称规则,即
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[6][6],n=5,cnt=0;
for(int i=1; i<=n; i++) {
for(int j=1; j<=(n+1)/2; j++) {
a[i][j]=a[i][n-j+1]=++cnt;
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码将数组赋值为:
1 2 3 2 1
4 5 6 5 4
7 8 9 8 7
10 11 12 11 10
13 14 15 14 13
b) 关于 上下对称

二维数组关于 上下对称,则行 不变, 方向成一维数组对称规则,即
int a[6][6],n=5,cnt=0;
for(int i=1; i<=(n+1)/2; i++) {
for(int j=1; j<=n; j++) {
a[i][j]=a[n-i+1][j]=++cnt;
}
}
上述代码将数组赋值为:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
6 7 8 9 10
1 2 3 4 5
c) 关于左斜对角线 对称

二维数组关于 斜对称的规则为, 和 坐标互换即
根据对角线对称规则,上面左斜矩阵四的代码还可以更简洁的写为:
int a[6][6],n=5,cnt=0;
for(int i=1; i<=n; i++) {
for(int j=i; j<=n; j++) {
a[i][j]=a[j][i]=n-(j-i);//对角线上下对称,可以同时赋值
}
}
/* 上面代码也可以写为以下形式
int a[6][6],n=5,cnt=0;
for(int i=1; i<=n; i++) {
for(int j=1; j<=i; j++) {
a[i][j]=a[j][i]=n-(i-j);
}
}
*/
上述代码将数组赋值为:
5 4 3 2 1
4 5 4 3 2
3 4 5 4 3
2 3 4 5 4
1 2 3 4 5
d) 关于右斜对角线 对称

二维数组关于 斜对称的规则为,先分别把 的 和 进行倒序对称成为 ,然后互换横纵坐标,得到关于红色对角线对称的点 即
根据对角线对称规则,上面右斜矩阵七的代码还可以更简洁的写为:
for(int i=1; i<=n; i++) {
for(int j=1; j<=n-i+1; j++) {
a[i][j]=a[n-j+1][n-i+1]=i+j-1;
}
}
上述代码将数组赋值为:
1 2 3 4 5
2 3 4 5 4
3 4 5 4 3
4 5 4 3 2
5 4 3 2 1
e) 关于中心点对称

char a[10][10],c='A';
int n=7;
for(int i=1; i<=(n+1)/2; i++) {
for(int j=1; j<=(n+1)/2; j++) {
a[i][j]=a[i][n-j+1]=a[n-i+1][j]=a[n-i+1][n-j+1]=c++;
}
}
上述代码将数组赋值为:
A B C D C B A
E F G H G F E
I J K L K J I
M N O P O N M //对称轴
I J K L K J I
E F G H G F E
A B C D C B A
对称轴
3、不等值对称原理
很多时候我们理解对称是指,两个元素根据某对称轴(点),两个数值完全等值对称,但有时候对称原理也可以应用到不等值的地方,例如根据对称轴(点)对称的两数的和(差)为固定值,那么只要知道这个对称公式,就可以来利用对称原理来进行快速赋值。

例如上图的一维数值赋值,可以写成如下形式:
int n=10,k=0,a[15]={0};
for(int i=1;i<=n/2;i++){//少循环一半
a[i]=i;
a[n-i+1]=n+1-a[i];
}
for(int i=1;i<=n;i++){//倒序遍历输出
cout<<a[i]<<" ";
}
上面的例子,就是利用不等值对称原理,让赋值的时间复杂度少了一半。
因为我们知道和关于中心点对称,且; 所以填完,即可直接求得;
本章的方阵填数,大部分都可以用这节的数组对称规则来进行优化,但本文目的在于学习讨论各种规律特性,所以在其他节可能并没有利用对称原理优化,请大家自行思考是否可以利用对称原理来进行优化。
参考例题:
四、二维数组环形规律

int a[10][10],n=7,cnt=0;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
int t=min({i,j,n-i+1,n-j+1});
a[i][j]=t;
}
}
上述代码将数组赋值为:
1 1 1 1 1 1 1
1 2 2 2 2 2 1
1 2 3 3 3 2 1
1 2 3 4 3 2 1
1 2 3 3 3 2 1
1 2 2 2 2 2 1
1 1 1 1 1 1 1
根据二维数组的环形规则,只要给定 即可立刻定位到对应的位置,定位规则如下:
环形值;
第 环的边长 ;
则该点在上边;
则该点在右边;
则该点在下边;
则该点在左边;

习题
五、二维数组旋转规律

根据上述规律可得: 向右旋转 或者向左旋转 向右选择 或者向左旋转 向右旋转 或者向左旋转
#include <bits/stdc++.h>
using namespace std;
void print(int x[][10],int n,int m) {//输出数组x[][]
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
cout<<setw(3)<<x[i][j];
}
cout<<endl;
}
cout<<endl;
}
int main() {
int a[10][10],b[10][10],c[10][10],n=5,m=5,k=0;
cin>>n>>m;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
a[i][j]= ++k;
b[j][n-i+1]=k;//向右旋转
c[m-j+1][i]=k;//向左旋转
}
}
print(a,n,m);//输出数组a[][]
print(b,m,n);//输出数组b[][]
print(c,m,n);//输出数组c[][]
return 0;
}
上述代码运行的输出结果为:
5 7 //输入5行7列矩阵
1 2 3 4 5 6 7 //a[][]数组
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 32 33 34 35
29 22 15 8 1 //a[][]往右翻转后得到的b[][]数组
30 23 16 9 2
31 24 17 10 3
32 25 18 11 4
33 26 19 12 5
34 27 20 13 6
35 28 21 14 7
7 14 21 28 35 //a[][]往左翻转后得到的c[][]数组
6 13 20 27 34
5 12 19 26 33
4 11 18 25 32
3 10 17 24 31
2 9 16 23 30
1 8 15 22 29
参考例题:
LQ1605CM5X. 矩阵圈层交错旋转(数据加强版)
六、其他填数规律
1、螺旋填数

a) 步行模拟法:
可以用模拟步行的算法进行填写:
//螺旋填数 模拟法
#include <bits/stdc++.h>
using namespace std;
int a[105][105];
int main(){
int n;
cin>>n;
//定义起点
int i=1,j=1;//起点坐标
int cnt=1; //起点数值
a[i][j]=cnt;//填写起点
while(cnt<n*n){//当cnt小于n*n则继续填写
//while(cnt<n*n-cnt+1){//从cnt~n*n+cnt-1填数
//while(下一个格子没有出界&&没有填数)//i,j-> i-1,j
while(j+1<=n && a[i][j+1]==0)a[i][++j]=++cnt;//往右
while(i+1<=n && a[i+1][j]==0)a[++i][j]=++cnt;//往下
while(j-1>=1 && a[i][j-1]==0)a[i][--j]=++cnt;//往左
while(i-1>=1 && a[i-1][j]==0)a[--i][j]=++cnt;//往上
}
//输出
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j]<<" ";
}cout<<endl;
}
return 0;
}
上述输出结果为:
5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
上述代码只需要更改起点信息,和移动方向,即可生成各种回型图
//回形填数
#include <bits/stdc++.h>
using namespace std;
int a[105][105],s,cnt;//s:起点数值 cnt:计数值
int main(){
int n;
cin>>n>>s;
//定义起点
int i=n,j=1;//起点坐标
a[i][j]=cnt=s;//填写起点
//while(cnt<n*n){//当cnt小于n*n则继续填写
while(cnt<n*n+s-1){//从cnt~n*n+cnt-1填数
//while(下一个格子没有出界&&没有填数)//i,j-> i-1,j
while(i-1>=1 && a[i-1][j]==0)a[--i][j]=++cnt;//往上
while(j+1<=n && a[i][j+1]==0)a[i][++j]=++cnt;//往右
while(i+1<=n && a[i+1][j]==0)a[++i][j]=++cnt;//往下
while(j-1>=1 && a[i][j-1]==0)a[i][--j]=++cnt;//往左
}
//输出
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j];
}cout<<endl;
}
return 0;
}
上述输出结果为:
5 11
15 16 17 18 19
14 29 30 31 20
13 28 35 32 21
12 27 34 33 22
11 26 25 24 23
b) 坐标法

令当前坐标位置为 ,则它的右,下,左,上的坐标分别是 ,,,。
//螺旋填数 坐标法
#include <bits/stdc++.h>
using namespace std;
int a[105][105],n;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int main(){
memset(a,-1,sizeof(a));//设置矩阵围墙,防止出界
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=0;//初始化
}
}
int x=1,y=1;//起点坐标
int d=0;//起始方向
for(int k=1;k<=n*n;k++){
a[x][y]=k;
if(a[x+dx[d]][y+dy[d]]!=0)d=(++d)%4;//遇到不能填数了,则转向
x+=dx[d];//朝着下一个d方向填数
y+=dy[d];
}
//输出
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j];
}cout<<endl;
}
return 0;
}
c) 利用二维数组环形规律填数
//螺旋填数 环形规则
#include <bits/stdc++.h>
using namespace std;
int n,a[105][105],s,cnt;//cnt:计数值
int main(){
cin>>n;
s=(n+1)/2;//环总数
for(int t=1;t<=s;t++){//t:环的序号
int len=n-(t-1)*2-1; //第t环的边长
if(len==0)len++;//最内圈如果为1个数的时候上一行求得的len会得0,所以需要+1;
int x=t,y=t;//每一圈的起点位置
for(int i=1;i<=len;i++){//第一条上边长:往右走的边长
a[x][y++]=++cnt;
}
for(int i=1;i<=len;i++){//第二条右边长:往下走的边长
a[x++][y]=++cnt;
}
for(int i=1;i<=len;i++){//第三条下边长:往左走的边长
a[x][y--]=++cnt;
}
for(int i=1;i<=len;i++){//第四条左边长:往往走的边长
a[x--][y]=++cnt;
}
//上面四条边可以写成一个for循环
}
//输出
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j];
}cout<<endl;
}
return 0;
}
上述输出结果为:
5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
d) 公式法
螺旋方阵填数的最高效填法为:利用环形规则,根据给定的坐标 直接即可计算得出该点的数值。

如上图所示,令,根据的值可以求得:
该点所在的环数 ;
环的边长 得(注意如果是最里面的中心圈是否需要特判的情况)
这里讨论的边长len见下图中的图示范围,即为实际边长-1;
如果知道第环的起点或者终点数值,然后再求得在该环上的增量值;那么就可以直接求得。
那如何求得第环的起点或者终点值呢?
第一种方法:递推法求环的起点和终点值
int st[105] {0},endd[105]= {0},len;
//st[t]:表示第t环的第一个数
//endd[t]:表示第t环的最后一个数
st[1]=1;
for(int t=1; t<=(n+1)/2; t++) {
len=n-(t-1)*2-1;//边长
st[t+1]=st[t]+len*4;
//endd[t]=st[t+1]-1;
}
for(int t=1; t<=n/2; t++) {
len=n-(t-1)*2-1;//边长
endd[t]=end[t-1]+len*4;
//st[t+1]=endd[t]+1;
}
第二种方法:公式法求环的起点或者终点值
根据上图可知:
设第圈的边长为 ;
设填完圈后的最后一个数为:
第圈总数第圈总数第圈总数第圈总数
$=(k-0)\times4+(k-2)\times4+(k-4)\times4+...+(k-2\times(t-1))\times4$
第圈开始的第一个数为:
接下来求在第t环上的顺序偏移量值:
int t=min({i,j,n-i+1,n-j+1});//t环由外而内的序号
int len=n-(t-1)*2-1;//a[i][j]//所在环的边长
if(i==min(t,n-t+1)&&j<n-t+1) { //该点位于上面的绿色边上
d=j-i+1;//d=j-t+1;//t==i
}
if(i==max(t,n-t+1)&&j>t) { //该点位于下面的橙色边上
d=len*2+i-j+1;//d=len*2+(n-t+1)-j+1;//n-t+1==i
}
if(j==min(t,n-t+1)&&i>t) { //该点位于左面的红色边上
d=len*3+(n-j+1)-i+1;//d=len*3+(n-t+1)-i+1;//t==j
}
if(j==max(t,n-t+1)&&i<n-t+1) { //该点位于右面的蓝色边上
d=len+i-t+1;//t==n-j+1
}
综上,;
代码实现如下:
//螺旋填数 公式法
#include <bits/stdc++.h>
using namespace std;
int n,a[105][105],t,len,s,d,endd,st;//cnt:计数值
int main() {
cin>>n;
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
t=min({i,j,n-i+1,n-j+1});//t:环由外而内的序号
len=n-(t-1)*2-1;//a[i][j]//所在环的边长
endd=4*t*(n-t);//第t圈结束的值
st=endd-len*4;//第t-1圈结束的值
//求在t环上的偏移量d值
d=1;//当n为奇数时候,考虑中心点时候下面if语句d值不计算,所以初始化为1
//此时也可以把求d值的if语句中的 && j<n-t+1 &&j>t &&i<n-t+1删除,但是&&i>t不能删除
if(i==min(t,n-t+1)&&j<n-t+1) { //该点位于上面的绿色边上
d=j-i+1;//d=j-t+1;//t==i
}
if(i==max(t,n-t+1)&&j>t) { //该点位于下面的橙色边上
d=len*2+i-j+1;//d=len*2+(n-t+1)-j+1;//n-t+1==i
}
if(j==min(t,n-t+1)&&i>t) { //该点位于左面的红色边上
d=len*3+(n-j+1)-i+1;//d=len*3+(n-t+1)-i+1;//t==j
}
if(j==max(t,n-t+1)&&i<n-t+1) { //该点位于右面的蓝色边上
d=len+i-t+1;//t==n-j+1
}
a[i][j]=st+d;
}
}
//输出
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
以上代码运行结果:
5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
利用数组对称规则,可以让代码变得更简洁,我们先观察以下代码运行后的结果:
//螺旋填数
#include <bits/stdc++.h>
using namespace std;
int n,a[105][105],t,len,s,d,endd,st;//cnt:计数值
int main() {
cin>>n;
for(int i=1; i<=(n+1)/2; i++) {
for(int j=i; j<=n-i+1; j++) {
t=min({i,j,n-i+1,n-j+1});//t:环由外而内的序号
len=n-(t-1)*2-1;//a[i][j]//所在环的边长
endd=4*t*(n-t);//第t圈结束的值
st=endd-len*4;//第t-1圈结束的值
d=j-i+1;//偏移量d值
a[i][j]=st+d;
}
}
//输出
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
以上代码运行结果:
5
1 2 3 4 5
0 17 18 19 0
0 0 25 0 0
0 0 0 0 0
0 0 0 0 0
如果第9行改为,则运行结果为:
5
1 2 3 4 0
0 17 18 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
我们只需要计算出上面1/4部分的数据,其他3/4部分可以利用对称规律直接得出:
//螺旋填数
#include <bits/stdc++.h>
using namespace std;
int n,a[105][105],t,len,s,d,endd,st;//cnt:计数值
int main() {
cin>>n;
for(int i=1; i<=(n+1)/2; i++) {
for(int j=i; j<=n-i+1; j++) {
t=min({i,j,n-i+1,n-j+1});//t:环由外而内的序号
len=n-(t-1)*2-1;//a[i][j]//所在环的边长
endd=4*t*(n-t);//第t圈结束的值
st=endd-len*4;//第t-1圈结束的值
d=j-i+1;//偏移量d值
a[i][j]=st+d;//上面的绿色边
//利用对称规律直接得出剩下三个数
a[j][n-i+1]=st+len+d;//右面的蓝色边
a[n-i+1][n-j+1]=st+len*2+d;//下面的橙色边
if(i+j!=n+1){//思考:如果没有这句会怎样?
a[n-j+1][i]=st+len*3+d;//左面的红色边
}
}
}
//输出
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
第9行如果循环少一次,则可以改写为:
//螺旋填数
#include <bits/stdc++.h>
using namespace std;
int n,a[105][105],t,len,s,d,endd,st;//cnt:计数值
int main() {
cin>>n;
for(int i=1; i<=(n+1)/2; i++) {
for(int j=i; j<=n-i; j++) {//比上面代码少循环一次
t=min({i,j,n-i+1,n-j+1});//t:环由外而内的序号
len=n-(t-1)*2-1;//a[i][j]//所在环的边长
endd=4*t*(n-t);//第t圈结束的值
st=endd-len*4;//第t-1圈结束的值
d=j-i+1;//偏移量d值
a[i][j]=st+d;//上面的绿色边
//利用对称规律直接得出剩下三个数
a[j][n-i+1]=st+len+d;//右面的蓝色边
a[n-i+1][n-j+1]=st+len*2+d;//下面的橙色边
a[n-j+1][i]=st+len*3+d;//左面的红色边
}
}
a[(n+1)/2][(n+1)/2]=n*n;//当n为奇数时候,最后一圈要单独赋值
//输出
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
例题:
2、S形填数
S形如下图:

1、模拟法
模拟法1
//起点位线内
#include <bits/stdc++.h>
using namespace std;
int n,a[105][105],x,y,k;
int main(){
cin>>n;
x=1;y=1;k=1;a[x][y]=k;
while(k<n*n){
while(x-1>=1 && y+1<=n)a[--x][++y]=++k;//往右上
if(x+y<=n)a[x][++y]=++k;
else a[++x][y]=++k;
while(x+1<=n && y-1>=1)a[++x][--y]=++k;//往左下
if(x+y<=n)a[++x][y]=++k;
else a[x][++y]=++k;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
模拟法2
////起点位线外
#include <bits/stdc++.h>
using namespace std;
int a[105][105],n;
int main(){
cin>>n;
int i=0,j=1,k=0; //起点
//int n,i=1,j=1,k=1; a[1][1]=k;//起点
while(k<n*n){
if(i+j<n+1){
i+=2;j--;
}else{
i++;
}
while(i-1>=1 && j+1<=n) a[--i][++j]=++k; //往右上
if(i+j<n+1){
i--;j+=2;
}else{
j++;
}
while(i+1<=n && j-1>=1) a[++i][--j]=++k; //往右上
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
2、对角线原理填数
利用对角线原理遍历填数1
#include <bits/stdc++.h>
using namespace std;
int a[55][55],n,cnt=0;
int main() {
cin>>n;
for(int p=1;p<=2*n-1;p++){//右斜对角线 p: i+j=1
int m,i,j;//对角线长度为m,坐标(i,j)
if(p<=n){//对角线p左上角
m=p;i=p;j=1;//起点坐标(i,j)
}else{//对角线p右下角
m=2*n-p;i=n;j=p-n+1;//起点坐标(i,j)
}
for(int q=1;q<=m;q++){//每条对角线的长度为 m
if(p%2)a[i--][j++]=++cnt;//p奇数则左下往右上方向填数
else a[j++][i--]=++cnt;//p偶数则右上往左下方向填数
}
}
//输出
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
利用对角线原理遍历填数2
#include <bits/stdc++.h>
using namespace std;
int i,j,n,m,a[20][20] = {0},t=0;
int main(){
cin>>n;
for(i = 1;i<=2*n-1;i++){//斜向的行数i
if(i<=n)m=i; //左上角
else m=n-i%n; //右下角
for(j = 1;j <=m;j++){//斜向的列数j m=1,2...n...2,1;
if(i<=n){//填写左上角
if(i%2){
a[i-j+1][j]=++t;//奇数行
}else{
a[j][i-j+1] = ++t;//偶数行
}
}
else{//填写右下角
if(i%2){
a[n-j+1][i%n+j]=++t;//奇数行
}else{
a[i%n+j][n-j+1]=++t;//偶数行
}
}
}
}
//输出
for(i = 1;i <= n;i++){
for(j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
3、公式法
全网最高效解法:直接计算求解
#include <bits/stdc++.h>
using namespace std;
int n,a[20][20] = {0},k,s=0;
int main(){
cin>>n;
for(int i=1;i<=n;i++){//第i行
for(int j=1;j<=n;j++){//第j列
k=i+j-1;//右斜对角线序号k
if(k>n){//右下角
//求k行之前已经填写的个数总和,即s=1+2+3+...+n+(n-1)+...+(2*n-k);
s=n*n-(1+(2*n-1-k+1))*(2*n-1-k+1)/2;
if(k%2) a[i][j]=s+j-k%n;//奇数行计算公式
else a[i][j]=s+i-k%n; //偶数行计算公式
}else{//左上角
//求k行之前已经填写的个数总和,即s=1+2+3+...+(k-1)
s=(1+(k-1))*(k-1)/2;
if(k%2) a[i][j]=s+j;//奇数行计算公式
else a[i][j]=s+i;//偶数行计算公式
}
}
}
//输出
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
上述代码如果利用不等值对称原理进行优化,将使问题变得更简单:
我们只需要考虑填写左上角数值,然后右下角数值可以直接利用对称原理求得。

优化后的代码:
#include <bits/stdc++.h>
using namespace std;
int n,a[20][20] = {0},k,s=0;
int main(){
cin>>n;
for(int i=1;i<=n;i++){//第i行
for(int j=1;j<=n-i+1;j++){//第j列
k=i+j-1;//右斜对角线序号k
//求k行之前已经填写的个数总和,
//即s=1+2+3+...+(k-1)=(1+(k-1))*(k-1)/2=k*(k-1)/2;
s=k*(k-1)/2;
a[i][j]=s+!(k%2)*i+(k%2)*j;
a[n-i+1][n-j+1]=n*n+1-a[i][j];
}
}
//输出
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
cout<<setw(3)<<a[i][j];
}
cout<<endl;
}
return 0;
}
/*第12行代码的解释
if(k%2){
a[i][j]=s+j;//奇数行计算公式
a[n-i+1][n-j+1]=n*n+1-a[i][j];//利用对称原理填写右下角的值
}else{
a[i][j]=s+i;//偶数行计算公式
a[n-i+1][n-j+1]=n*n+1-a[i][j];//利用对称原理填写右下角的值
}
*/
例题:
1 条评论
-
gf296305 LV 2 @ 2026-8-26 15:01:02把我成绩删了,你完蛋!!!!!!
- 1