【每日挠头算法题】Acwing 756. 蛇形矩阵 —— 巧妙解法

👑作者主页:@进击的安度因
🏠学习社区:进击的安度因(个人社区)
📖专栏链接:每日挠头算法题

如果无聊的话,就来逛逛 我的博客栈 吧! 🌹

一、题目描述

链接756. 蛇形矩阵

输入两个整数 nm ,输出一个 nm 列的矩阵,将数字 1n × m 按照回字蛇形填充至矩阵中。

具体矩阵形式可参考样例。

输入格式

输入共一行,包含两个整数 nm

输出格式

输出满足要求的矩阵。

矩阵占 n 行,每行包含 m 个空格隔开的整数。

数据范围

1 ≤ n, m ≤ 100

输入样例

3 3

输出样例

1 2 3
8 9 4
7 6 5

二、思路讲解

蛇形矩阵,就是将数字以 回字形 填充到 二维数组 中,比如这样:

image-20221213204831664

我们把 二维数组的行 看做 x轴二维数组的列 看做 y轴

结合数轴,我们其实可以发现蛇形矩阵填数的规律:从左上角第一个元素开始,先向右填,碰边;向下填,碰边;向左填,碰边;向上填,碰到已经填过的位置,退回原位;向右填 … 之后就是重复上面的规律来填充。

通过规律可以发现 蛇形矩阵 填数的其实是和 x, y 两个轴是息息相关的,我们将数据的坐标记为 (x, y)

  • 向右走:(x, y) --> (x, y + 1),纵坐标 + 1
  • 向下走:(x, y) --> (x + 1, y),横坐标 + 1
  • 向左走:(x, y) --> (x, y - 1),纵坐标 - 1
  • 向上走:(x, y) --> (x - 1, y),横坐标 - 1

而上面四种移动在 题目中的具体表现 就像下面这样:

  1. x 不动 - x = 0y 向右移 - y + 1
  2. y 不动 - y = 0x 向下移 - x + 1
  3. x 不动 - x = 0y 向左移 - y - 1
  4. y 不动 - y = 0x 向上移 - x - 1

所以 xy 的坐标变化就分别有 4 种。便可以把这些移动方式存入数组:dx[] = { 0, 1, 0, -1 }, dy[] = { 1, 0, -1, 0 }

从开始填数到填数完成需要改变很多次方向,那么什么情况需要改变填数方式?

  • 填数越过左边界
  • 填数越过右边界
  • 填数越过上边界
  • 填数越过下边界
  • 填数位置已有数据

第五点 是最需要注意的,其实后面大多都是第五种情况,前四种情况基本只会在 最外一圈 用到。

在里层 碰到填过的数据 就得 “回退并拐弯” ,以免覆盖填过的数据。

到这儿主题思路其实已经讲完了,下面再讲一下细节

如果坐标 xy “飙错位置” ,如何把它 “拉回正确位置” ?可以用 ab 变量分别记录当前坐标在移动后是否超出范围,重新调整 移动方法 ,实际上就是重新选取 dxdy 数组中 合适的元素 ,然后重新计算 ab ,将坐标 拉回来 ,做出正确调整,再将 ab 赋给 xy

下面我们看看代码怎么写。

三、代码实现

#include <iostream>

using namespace std;

const int N = 110;

// 全局中定义数组,元素默认初始化为0
int q[N][N];

int n, m;

int main()
{
    cin >> n >> m;
    
    int dx[] = { 0, 1, 0, -1 }, dy[] = { 1, 0, -1, 0 };
    
    // 起始 x 和 y 在 (0, 0),并且 d 为 0,对应着 x 不动,y 往右走
    int x = 0, y = 0, d = 0;
    
    for (int i = 1; i <= n * m; i++)
    {
        q[x][y] = i;
        
        // 计算 a, b 的下一个位置
        int a = x + dx[d], b = y + dy[d];
        
        // 判断是否超限
        // 这里 q[a][b] 其实有一层妙用,由于全局数组是被初始化 0 的,
        // 只要填过数,q[a][b] 就必定为真
        if (a < 0 || a >= n || b < 0 || b >= m || q[a][b])
        {
            // 移动到下一个位置,% 4 获取 [0, 3] 下标
            d = (d + 1) % 4;
            a = x + dx[d], b = y + dy[d];
        }
        
        // 将正确的 a b 赋给 x y
        x = a, y = b;
    }
    
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cout << q[i][j] << " ";
        }
        cout << endl;
    }
    
    return 0;
}
评论 58
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安 度 因

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值