【题目链接】
【题目考点】
1. 二维数组
【解题思路】
解法1:移动焦点
设焦点位置,焦点移动同时为焦点位置赋值。焦点移动规则为:
- 移动到右上角时,行加1
- 移动到左下角时,列加1
- 移动到第一行或第n行时,列加1
- 移动到第一列或第n列时,行加1
不断移动焦点,填充整个矩阵,最后输出矩阵。
解法2:遍历矩阵
下标从1开始,每条右上左下斜线上的位置的行列坐标加和是一定的,第一条斜线的加和为2,第二条为3,。。。,最后一条即第2n-1条斜线加和为2n。每次遍历整个矩阵,为一条斜线赋值。
观察规律可知:
- 斜线编号为奇数的斜线,数字是从左下向右上填充的,行减少列增加。
- 斜线编号为偶数的斜线,数字是从右上向左下填充的,行增加列减少。
每次填充一条斜线,填充整个矩阵,最后输出矩阵。
解法3:数学计算
斜线值:每条右上左下斜线上的位置的行号与列号的加和是相同的,记该加和为斜线值。例:(i, j)所在斜线的斜线值为i+j。
该解法可以通过位置的行号i、列号j直接求出(i, j)位置的值。
1. 对于左上方三角矩阵(包括副对角线)中的某位置(i, j)
该矩阵为n*n的矩阵,因此副对角线(右上左下)的斜线值为n+1。
因此左上方三角矩阵(包括副对角线)中的所有斜线值满足:
i
+
j
≤
n
+
1
i+j\le n+1
i+j≤n+1
对于左上方三角矩阵(包括副对角线)中的某位置(i, j),其所在斜线的值
k
=
i
+
j
k=i+j
k=i+j
考虑该斜线左上方(不包括该斜线)的位置数:
第1条斜线,斜线值为2,有1个元素,
第2条斜线,斜线值为3,有2个元素,
。。。
第k-2条斜线,斜线值k-1,有k-2个元素。
共有元素:
1
+
2
+
.
.
.
+
k
−
2
=
(
k
−
1
)
∗
(
k
−
2
)
/
2
1+2+...+k-2 = (k-1)*(k-2)/2
1+2+...+k−2=(k−1)∗(k−2)/2个
因此从(1,1)以蛇形方式走到斜线值为k的斜线前,共经过元素
(
k
−
1
)
∗
(
k
−
2
)
/
2
(k-1)*(k-2)/2
(k−1)∗(k−2)/2个位置,已经填充了数字
1
1
1到
(
k
−
1
)
∗
(
k
−
2
)
/
2
(k-1)*(k-2)/2
(k−1)∗(k−2)/2。
再考虑到达斜线值为k的斜线上后走到(i,j)所经过的元素个数
- 如果k是偶数,则为从左下向右上走,从(i+j-1,1)走到(i,j)时走过的数字为j个
- 如果k是奇数,则为从右上向左下走,从(1,i+j-1)走到(i,j)时走过的数字为i个
因此(i,j)位置的值为(k-1)*(k-2)/2+(k%2==0?j:i)
。
2. 对于右下方三角矩阵(不包括副对角线)中的某位置(i, j)
对于右下方三角矩阵(不包括副对角线)中的某位置(i, j),其所在斜线的值
k
=
i
+
j
k=i+j
k=i+j
考虑该斜线左上方(不包括该斜线)的位置数:
第1条斜线,斜线值为2,有1个元素,
第2条斜线,斜线值为3,有2个元素,
。。。
第n条斜线,斜线值为n+1,有n个元素
第n+1条斜线,斜线值为n+2,有n-1个元素
第n+2条斜线,斜线值为n+3,有n-2个元素
。。。(规律:斜线值和该线上元素个数加和为2*n+1)
第k-2条斜线,斜线值k-1,有
(
2
∗
n
+
1
−
(
k
−
1
)
)
=
2
∗
n
+
2
−
k
(2*n+1-(k-1))=2*n+2-k
(2∗n+1−(k−1))=2∗n+2−k个元素。
前n条斜线的元素数量:
(
1
+
n
)
n
/
2
(1+n)n/2
(1+n)n/2
第n+1条斜线到第k-2条斜线的元素数量:
(
n
−
1
+
2
∗
n
+
2
−
k
)
∗
(
k
−
2
−
(
n
+
1
)
+
1
)
/
2
=
(
3
∗
n
+
1
−
k
)
∗
(
k
−
n
−
2
)
/
2
(n-1+2*n+2-k)*(k-2-(n+1)+1)/2=(3*n+1-k)*(k-n-2)/2
(n−1+2∗n+2−k)∗(k−2−(n+1)+1)/2=(3∗n+1−k)∗(k−n−2)/2
再考虑到达斜线值为k的斜线上后走到(i,j)所经过的元素个数
- 如果k是偶数,则为从左下向右上走,从(n,i+j-n)走到(i,j)时走过的数字为n-i+1个
- 如果k是奇数,则为从右上向左下走,从(i+j-n,n)走到(i,j)时走过的数字为n-j+1个
因此(i,j)位置的值为(1+n)n/2+(3*n-k+1)*(k-n-2)/2+(k%2==0?n-i+1:n-j+1)
。
另一种求右下方三角矩阵(不包括副对角线)中的某位置(i, j)的值的方法:可以取矩阵中与(i,j)中心对称的位置(n-i+1, n-j+1),这两个位置的加和一定为n*n+1。
因此a[i][j] = n*n+1-a[n-i+1][n-j+1]
【题解代码】
解法1:移动焦点
#include<bits/stdc++.h>
using namespace std;
int a[15][15], dir[2][2]={{-1,1},{1,-1}};//两种方向:右上、左下
int main()
{
int k = 1, n, i, j, d = 0, si, sj;//(si,sj):拟定的下一个位置
cin >> n;
i = j = 1;//(i,j):当前位置
while(k <= n*n)
{
a[i][j] = k++;
si = i + dir[d][0], sj = j + dir[d][1];
if(si >= 1 && si <= n && sj >= 1 && sj <= n)//如果下一个位置在边界内
i += dir[d][0], j += dir[d][1];
else//下一个位置在边界外
{
d = (d+1)%2;//改变移动方向
if(i == 1 && j != n || i == n)//如果在第一行或最后一行(不包括右上角),到下一列
j++;
else if(j == 1 && i != n || j == n)//如果在第一列或最后一列(不包括左下角),到下一行
i++;
}
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}
解法2:遍历矩阵
#include <bits/stdc++.h>
using namespace std;
int a[15][15];
int main(){
int n, k = 1;
cin >> n;
for(int m = 1; m <= 2*n-1; m++)//m为斜线编号,即每条斜线的行列坐标加和减1
{
if(m%2 == 0)//斜线编号为偶数的斜线,数字是从右上向左下填充的,行增加列减少。
{
for(int i = 1; i <= n; i++)
for(int j = n; j >= 1; j--)
if(i + j == m + 1)//第m条斜线的行列坐标加和为m+1
a[i][j] = k++;
}
else//斜线编号为奇数的斜线,数字是从左下向右上填充的,行减少列增加。
{
for(int i = n; i >= 1; --i)
for(int j = 1; j <= n; ++j)
if(i + j == m + 1)
a[i][j] = k++;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}
解法3:数学计算
- 方法1:规律推导
#include<bits/stdc++.h>
using namespace std;
#define N 105
int main()
{
int n, k, a[N][N];
cin >> n;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
{
k = i+j;//斜线编号
if(k <= n+1)
a[i][j] = (k-1)*(k-2)/2+(k%2==0?j:i);
else
a[i][j] = (1+n)*n/2+(3*n-k+1)*(k-n-2)/2+(k%2==0?n-i+1:n-j+1);
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}
- 方法2:利用中心对称
#include<bits/stdc++.h>
using namespace std;
#define N 105
int getVal(int i, int j)//获取左上方三角形矩阵中(i,j)位置的值
{
return (i+j-1)*(i+j-2)/2+((i+j)%2 == 0 ? j : i);
}
int main()
{
int a[N][N], n, k;
cin >> n;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
{
k = i+j;//斜线编号
if(k <= n+1)
a[i][j] = getVal(i, j);
else
a[i][j] = n*n+1-getVal(n-i+1, n-j+1);
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}