2014NOIP普及组真题 3. 螺旋矩阵

线上OJ:

一本通:http://ybt.ssoier.cn:8088/problem_show.php?pid=1967

背景知识:

螺旋矩阵可以采用模拟的方式生成。就是顺时针四个方向

第1步、是第 1 行,方向为从左到右,数值+1。当向遇到 边界n 或者 格子已填过数值 时停止
第2步、是第 n 列,方向为从上到下,数值+1。当向遇到 边界n 或者 格子已填过数值 时停止
第3步、是第 n 行,方向为从右到左,数值+1。当向遇到 边界0 或者 格子已填过数值 时停止
第4步、是第 1 列,方向为从下到上,数值+1。当向遇到 边界0 或者 格子已填过数值 时停止

以上四步为一轮。然后开始下一轮,直至数值填满达到 n*n 为止。

本题的第一种常规解法,是先生成螺旋矩阵,然后输出其中的数值。但是题中的n会达到30000,此时开a[n][n]的二位矩阵在空间上会爆,所以这种方法只能拿50分。

代码如下:

解法一(50%分数)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10005;
int a[maxn][maxn];
int n, i ,j;
int dx[4]={0, 1, 0, -1};
int dy[4]={1, 0, -1, 0};

void cal() {
    int x = 0, y = 0;
    a[x][y] = 1;
    for (int k = 2; k <= n * n; )
    {
        for(int t = 0; t < 4; t++)
        {
            while (1)
            {
                int nx = x + dx[t], ny = y + dy[t];
                if (nx < 0 || nx >= n || ny < 0 || ny >= n || a[nx][ny])  break;

                a[x = nx][y = ny] = k++;
            }
        }     
    }
}
int main() {
    cin >> n >> i >> j;
    cal();
    cout << a[i-1][j-1];
    return 0;
}

以上代码的二位矩阵会爆,故需要进行优化。由于本题中只需要输出 a[i][j] 的值,并不需要生成整个螺旋矩阵。故可以考虑通过递归来计算 a[i][j]。

核心思想:

由于螺旋矩阵每次只能顺序计算最外圈,故我们可以根据矩阵的 n 和 i j 推断出最外圈每个位置的计算公式(如下图所示)。

在这里插入图片描述

公式1(外圈数值):

第 1 行, a [ 1 ] [ j ] = j a[1][j] = j a[1][j]=j // 如果剥离外层后,所求点处于当前矩阵的第一行,则行坐标就是值
第 n 列, a [ i ] [ n ] = n + i − 1 a[i][n] = n+i-1 a[i][n]=n+i1 // 如果剥离外层后,所求点处于当前矩阵的最后一列,则 i+n-1 就是值(从上往下走)
第 n 行, a [ n ] [ j ] = 3 n − j − 1 a[n][j] = 3n - j - 1 a[n][j]=3nj1 // 如果剥离外层后,所求点处于当前矩阵的最后一行,则 2n-1+n-j = 3n-j-1 就是值
第 1 列, a [ i ] [ 1 ] = 4 n − i − 2 a[i][1] = 4n-i-2 a[i][1]=4ni2 // 如果剥离外层后,所求点处于当前矩阵的第一列,则 3n-2+n-i= 4n-i-2就是值

光有外圈的计算公式还不足以计算内圈a[i][j],需要知晓每次剥去外圈向内传递时的差值。

在这里插入图片描述

从上图中,我们可以发现,a[1][1]=1,传到a[2][2]的增量为当前 n-1 的4倍

比如:n=9 时,增量为4*(9-1)=32
比如:n=7 时,增量为4*(7-1)=24
比如:n=5 时,增量为4*(5-1)=16

综上所述,可以先深搜 a[i][j] 达到最外层,使其可直接计算。然后再层层递归回来,每次**递归回时加上增量 4(n-1) **即可。
代码如下

解法二、dfs(100%)
#include <bits/stdc++.h>
using namespace std;

int n, i, j;

// 剥离外圈向内传递时,n减少2(因为行、列各减少2),i和j各减少1
// a[1][1]到a[2][2]的增量为 (n-1)*4
int dfs(int n, int i, int j)
{
    if(i == 1)  return j;             // 如果剥离外层后,所求点处于当前矩阵的第一行,则行坐标就是值
    if(j == n)  return n + i - 1;     // 如果剥离外层后,所求点处于当前矩阵的最后一列,则 i+n-1 就是值(从上往下走)
    if(i == n)  return 3 * n - j - 1; // 如果剥离外层后,所求点处于当前矩阵的最后一行,则 2n-1+n-j = 3n-j-1 就是值
    if(j == 1)  return 4 * n - i - 2; // 如果剥离外层后,所求点处于当前矩阵的第一列,则 3n-2+n-i= 4n-i-2就是值

    return dfs(n-2, i-1, j-1) + (n-1)*4;  // 如果剥离外层后,所求点还没到外圈,则继续剥离本圈
}

// 在已知 n 的前提下,最外圈的坐标可以直接求出
// 如果不在最外圈,就一层一层剥离,直到所求点处于最外圈
int main()
{
    cin >> n >> i >> j;
    cout << dfs(n, i, j);
    return 0;
}

在解法二中采用了深搜,当 a[i][j] 处于内圈时,依然有较高的复杂度。实际上,当我们在分析图二时,已经可以发现,a[1][1]可以一直向下传递。

在这里插入图片描述

其中,
1、a[1][1] → a[2][2],增量为 32,即 4*(9-1) = 4*(n-1)
2、a[2][2] → a[3][3],增量为 24,即 4*(9-1) - 8 = 4*(n-1) - 8。所以 a [ 3 ] [ 3 ] = a [ 2 ] [ 2 ] + 4 ∗ ( n − 1 ) − 8 = 2 ∗ 4 ∗ ( n − 1 ) − 8 a[3][3] = a[2][2]+ 4*(n-1) - 8 = 2* 4*(n-1) - 8 a[3][3]=a[2][2]+4(n1)8=24(n1)8
3、a[3][3] → a[4][4],增量为 16,即 4*(9-1) - 8 - 8 = 4*(n-1) - 28。所以 a [ 4 ] [ 4 ] = a [ 3 ] [ 3 ] + 4 ∗ ( n − 1 ) − 2 ∗ 8 = 3 ∗ 4 ∗ ( n − 1 ) − 3 ∗ 8 a[4][4] = a[3][3]+ 4*(n-1) - 2*8 = 3* 4*(n-1) - 3*8 a[4][4]=a[3][3]+4(n1)28=34(n1)38
4、a[4][4] → a[5][5],增量为 8,即 4
(9-1) - 8 - 8-8 = 4*(n-1) - 3*8。所以 a [ 5 ] [ 5 ] = a [ 4 ] [ 4 ] + 4 ∗ ( n − 1 ) − 3 ∗ 8 = 4 ∗ 4 ∗ ( n − 1 ) − 6 ∗ 8 a[5][5] = a[4][4]+ 4*(n-1) - 3*8 = 4* 4*(n-1) - 6*8 a[5][5]=a[4][4]+4(n1)38=44(n1)68
所以,剥离 k 层后的起始元素可表示为: k ∗ 4 ∗ ( n − 1 ) − ( k − 1 ) ∗ ( 8 + ( k − 1 ) ∗ 8 ) / 2 k*4*(n-1) - (k-1)*(8+(k-1)*8)/2 k4(n1)(k1)(8+(k1)8)/2
也就是说,我们不需要通过 dfs 也可以直接穿透到 a[i][j] 所在的那一个圈

公式2(剥离 k 层后的起始元素):
s t = k ∗ 4 ∗ ( n − 1 ) − ( k − 1 ) ∗ ( 8 + ( k − 1 ) ∗ 8 ) / 2 st = k*4*(n-1) - (k-1)*(8+(k-1)*8)/2 st=k4(n1)(k1)(8+(k1)8)/2

步骤如下(假设我们要计算是的 a[4][5]):

第一步:找到 a[4][5] 所在圈的第一个数值 a[4][4], 从a[1][1]可以直接推出
a [ 4 ] [ 4 ] = k ∗ 4 ∗ ( n − 1 ) − ( k − 1 ) ∗ ( 8 + ( k − 1 ) ∗ 8 ) / 2 a[4][4] = k*4*(n-1) - (k-1)*(8+(k-1)*8)/2 a[4][4]=k4(n1)(k1)(8+(k1)8)/2
第二步:在a[4][5] 所在圈内,按照公式1推出增量
第三步:前两步的数值相加

公式推导后的时间复杂度为O(1),代码见下

解法三、公式推导 O(1)
#include <bits/stdc++.h>
using namespace std;

int n, i, j;

// 剥离外圈向下传递时,n减少2(因为行、列各减少2),ℹ和j各减少1
int solve(int n, int i, int j)
{
    if(i == 1)  return j;             // 如果剥离外层后,所求点处于当前矩阵的第一行,则行坐标就是值
    if(j == n)  return n + i - 1;     // 如果剥离外层后,所求点处于当前矩阵的最后一列,则 i+n-1 就是值(从上往下走)
    if(i == n)  return 3 * n - j - 1; // 如果剥离外层后,所求点处于当前矩阵的最后一行,则 2n-1+n-j = 3n-j-1 就是值
    if(j == 1)  return 4 * n - i - 2; // 如果剥离外层后,所求点处于当前矩阵的第一列,则 3n-2+n-i= 4n-i-2就是值
}

// a[i][j] = st + increase
int main()
{
    cin >> n >> i >> j;
    if(i > n || j > n)
        return 0;
    else if(i == 1 || j == 1)
        cout << solve(n, i, j);
    else
    {
        int k = min(min(n-i, n-j), min(i, j) - 1); // a[i][j]处于第k层
        // 4(n-1)-0*8+4(n-1)-1*8+4(n-1)-2*8+4(n-1)-3*8 = k*4*(n-1) - (0*8+1*8+2*8+3*8+...(k-1)*8) = (k-1)(8+(k-1)*8)/2
        int st = k*4*(n-1) - (k-1)*(8+(k-1)*8)/2;

        cout << st + solve(n-2*k, i-k, j-k);
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/573402.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于卷积神经网络的手写数字识别

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

海外短剧:跨文化的新浪潮与看剧系统的搭建,海外短剧系统搭建开发定制

在全球化的大潮下&#xff0c;海外短剧作为一种新兴的文化交流方式&#xff0c;正逐渐受到越来越多人的喜爱。这种融合了各地文化元素、叙事手法新颖独特的短剧形式&#xff0c;不仅丰富了观众的视觉体验&#xff0c;也为影视媒体和想拓展海外市场的企业带来了无限商机。 一、…

MMSeg分析Flops和Params

Flops计算量&#xff0c;params参数量 在文件中 tools/analysis_tools/get_flops.py利用以下命令实现 python tools/analysis_tools/get_flops.py configs/xxx/xxx-Net.py后面可跟参数shape控制输入图片尺寸&#xff0c;例如 python tools/analysis_tools/get_flops.py conf…

Python Django框架的内容管理系统库之wagtail使用详解

概要 Python Wagtail库是一个基于Django框架的内容管理系统(CMS),它提供了丰富的功能和工具,帮助开发者快速构建灵活、强大的网站和应用。本文将介绍如何安装和使用Python Wagtail库,以及它的特性、基本功能、高级功能、实际应用场景和总结部分。 安装 首先,需要安装Py…

uni-app HBuilderX通过easycom省略import自动导入自定义组件

快速尝试 自HBuilderX 2.5.5起支持easycom组件模式。更新HBuilderX即可尝试。 easycom默认已启用&#xff0c;并对项目下的components和uni_modules目录开启自动扫描&#xff0c;对符合下面路径和命名规则的组件自动导入。 components/组件名/组件名.vue uni_modules/组件名/…

Go并发安全,锁和原子操作

一. 并发安全 有时候在Go代码中可能存在多个goroutine同时操作一个资源(临界区)&#xff0c;这种情况会发生竞态问题(数据竞态)。 1.1 互斥锁 互斥锁是一种常见的控制共享资源访问的方法&#xff0c;它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mut…

【日志】CSDN-AI助手升级日志

CSDN-AI助手升级日志 2023/04/05上线 支持点赞、收藏回访 关注回访&#xff08;对方至少有一条博客的记录&#xff09; 评论回访 私信检测到群发消息自动三连 OR 通过私信指令三连触发 bug优化 优化检测模式&#xff0c;防止出现多触发情况 为了防止操作额度不够&#xff0c…

Spring日志

Spring日志的作用: 1.定位和发现问题 2.系统监控 3.数据采集 4.日志审计 打印日志步骤: 1.定义日志对象2.打印日志 RestController public class LoggerController {private static Logger logger LoggerFactory.getLogger(LoggerController.class);PostConstructpublic v…

Dos慢速攻击

这里写自定义目录标题 Dos慢速攻击 Dos慢速攻击 测试结果为“Exit status&#xff1a; No open connections left"&#xff0c;代表无此漏洞。 如果测试结束后connected数量较多&#xff0c;closed数量很少或0&#xff0c;说明之前建立的慢速攻击测试连接没有关闭&#…

最佳三款员工电脑行为监控软件评选

企业对员工生产力和数据安全的关注不断增加&#xff0c;员工电脑行为监控软件成为了许多企业不可或缺的工具。 这些软件不仅可以帮助企业管理者实时监测员工的电脑使用情况&#xff0c;还可以防止数据泄露和滥用公司资源。 然而&#xff0c;市面上有数不胜数的员工电脑行为监控…

【前端缓存】localStorage是同步还是异步的?为什么?

写在开头 点赞 收藏 学会 首先明确一点&#xff0c;localStorage是同步的 一、首先为什么会有这样的问题 localStorage 是 Web Storage API 的一部分&#xff0c;它提供了一种存储键值对的机制。localStorage 的数据是持久存储在用户的硬盘上的&#xff0c;而不是内存。这意…

海外盲盒APP开发:探索海外盲盒市场的商机

随着娱乐消费的流行&#xff0c;盲盒在我国可以说是非常火热&#xff0c;消费群体和市场规模逐年增加。在盲盒热潮下&#xff0c;不少潮玩企业也纷纷加入到了盲盒赛道中&#xff0c;市场竞争非常激烈&#xff01; 此外&#xff0c;我国盲盒出海也成为了一个大趋势。盲盒不仅在…

如何用 Llama 3 免费本地 AI 分析数据和可视化?

帮助你消除调用大模型 API 带来的数据安全烦恼。 模型 今天我们来探讨一个有趣的话题 —— 如何使用 Llama 3 免费地进行数据分析和可视化。 Meta 团队在 2024 年 4 月发布了两款 Llama 3 新模型&#xff0c;一款是 8B&#xff0c;即 80 亿参数&#xff1b;另一款则是 70B&…

鸿蒙arkui 也支持热重载了 我不允许你不会

历史背景 因为鸿蒙出的ark ui 很多人说很像flutter&#xff0c;但是能不能做到跟flutter一样支持热重载呢 。答案是可以的 我们喜就教大家如何操作。 构建工程 选择这个空的模版 next finish 点击file 找到setting 找到 Tools Actions on Save 我们把 Perform hor reload 勾上…

说说你对二分查找的理解?如何实现?应用场景?

一、是什么 在计算机科学中&#xff0c;二分查找算法&#xff0c;也称折半搜索算法&#xff0c;是一种在有序数组中查找某一特定元素的搜索算法 想要应用二分查找法&#xff0c;则这一堆数应有如下特性&#xff1a; 存储在数组中有序排序 搜索过程从数组的中间元素开始&…

招商画册不会制作?这个教程收藏好

在制作招商画册过程中可能对于一些小型企业或刚刚起步的企业来说&#xff0c;招商画册的制作也不是一个难以克服的难题。本文将为您提供一些制作招商画册的技巧和建议。在制作招商画册前肯定是需要选择一款合适的在线制作工具&#xff0c;如FLBOOK 这个平台本就有海量的模板和设…

vue快速入门(四十四)自定义组件

注释很详细&#xff0c;直接上代码 上一篇 新增内容 全局注册自定义组件并应用局部注册自定义组件并应用 此篇使用了axios模块没有安装导入的先看这一篇 axios模块下载与导入 源码 main.js import Vue from vue import App from ./App.vue//全局引入axios // 引入axios impor…

超市火灾烟雾蔓延及人员疏散的matlab模拟仿真,带GUI界面

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 出口在人员的视野范围内时&#xff0c;该元胞选择朝向引导点的方向运动。出口不在人员的视野范围内时&#xff0c;作随机运动&#xff0c;8个方向的运动概率相等。…

网工交换基础——Qinq技术

一、Qinq的概述 QinQ&#xff08;802.1Q-in-802.1Q&#xff09;&#xff0c;也叫做VLAN Stacking或Double VLAN&#xff0c;由IEEE 802.1ad标准定义&#xff0c;是一项扩展VLAN空间的技术&#xff0c;通过在802.1Q标签报文的基础上再增加一层802.1Q的Tag来达到扩展VLAN空间的目…

Linux驱动开发——(六)按键中断实验

目录 一、简介 二、修改设备树 2.1 添加pinctrl节点 2.2 添加KEY设备节点 2.3 检查是否被其他外设使用 三、代码 3.1 驱动代码 3.2 测试代码 3.3 平台测试 一、简介 以I.MX6U-MINI为例&#xff0c;实现KEY0按下后&#xff0c;设备识别到并将数据发送到平台。 二、修…