OpenJudge NOI 1.8 24:蛇形填充数组
【题目链接】
OpenJudge NOI 1.8 24:蛇形填充数组
【题目考点】
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);elsea[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);elsea[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;
}