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+jn+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+...+k2=(k1)(k2)/2
因此从(1,1)以蛇形方式走到斜线值为k的斜线前,共经过元素 ( k − 1 ) ∗ ( k − 2 ) / 2 (k-1)*(k-2)/2 (k1)(k2)/2个位置,已经填充了数字 1 1 1 ( k − 1 ) ∗ ( k − 2 ) / 2 (k-1)*(k-2)/2 (k1)(k2)/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 (2n+1(k1))=2n+2k个元素。
前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 (n1+2n+2k)(k2(n+1)+1)/2=(3n+1k)(kn2)/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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值