题目描述

一个 n 行 n 列的螺旋矩阵可由如下方法生成:

从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动;如果前方是未曾经过的格子,则继续前进,否则右转;重复上述操作直至经过矩阵中所有格子。根据经过顺序,在格子中依次填入 1, 2, 3, ... , n ,便构成了一个螺旋矩阵。
下图是一个 n = 4 时的螺旋矩阵。
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7

现给出矩阵大小 n 以及 i 和 j ,请你求出该矩阵中第 i 行第 j 列的数是多少。

输入描述:

输入共一行,包含三个整数 n,i,j ,每两个整数之间用一个空格隔开,分别表示矩阵大小、待求的数所在的行号和列号。

输出描述:

输出一个整数,表示相应矩阵中第 i 行第 j 列的数。

输入

4 2 3

输出

14

备注:

对于 50% 的数据, 1 ≤ n ≤ 100 ;
对于 100% 的数据, 1 ≤ n ≤ 30,000,1 ≤ i ≤ n,1 ≤ j ≤ n

链接:https://ac.nowcoder.com/acm/problem/16502
来源:牛客网

1.暴力模拟法,o(n*2),只能通过50%的数据。

#include<iostream>
using namespace std; /***********************
1<=n<=30000无法用数组存储
***********************/ int n,i,j;
int top_bound,bottom_bound,left_bound,right_bound;
int count=1;
int direction=0; int main(){
cin>>n>>i>>j;
int k=1,m=1;
top_bound=1;
bottom_bound=n+1;
left_bound=0;
right_bound=n+1;
while(count<=n*n){
switch(direction){
case 0:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(m==right_bound){
//cout<<"reach right_bound "<<right_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
right_bound--;
m--;
k++;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
m++;
count++;
break; case 1:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(k==bottom_bound){
//cout<<"reach bottom_bound "<<bottom_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
bottom_bound--;
k--;
m--;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
k++;
count++;
break; case 2:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(m==left_bound){
//cout<<"reach left_bound "<<left_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
left_bound++;
m++;
k--;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
m--;
count++;
break; case 3:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(k==top_bound){
//cout<<"reach top_bound "<<top_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
top_bound++;
k++;
m++;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
k--;
count++;
break;
} }
return 0;
}

2. 外圈优化法,o(n),可通过100%的数据。

1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
假设想求得矩阵(2,3)位置上的值,可以从13-14-15-16这一内圈的开始点13作为锚点(anchor),模拟螺旋的方式计算出(2,3)的数。
13=4^2-2^2+1,即anchor= n*n-(inner_n*inner_n)+1,其中inner_n=n-2*dist,dist是内圈到外圈的距离;
anchor可以直接根据公式计算出,该算法主要的时间复杂度为模拟螺旋的时间,即数内圈的大小,o(inner_n)~o(n)
#include<iostream>
using namespace std; /***************************
1. 1 ≤ n ≤ 30000,因此无法将整个矩阵存储下来,只能输出目标结果
2. 模拟整个过程仍会超时,可将情况分为3类
(1)n==1, 输出1
(2)i,j==1|n,即该数在旋转矩阵的外围计算旋转矩阵边界上的值
(3)中间的数,根据首先计算外围数的个数,在外圈的数的基础上进行模拟
****************************/
int n,i,j;
int anchor=1,dist,anchor_pos=1; int min(int x,int y,int z,int q){
return min(min(min(x,y),z),q);
}
int min(int x,int y){
return x>y?y:x;
} int main(){
cin>>n>>i>>j;
if(n==1){
cout<<1<<endl;
}else{
int left,right,top,bottom;
left=j-1;
right=n-j;
top=i-1;
bottom=n-i;
//cout<<left<<" "<<right<<" "<<top<<" "<<bottom<<endl;
dist=min(left,right,top,bottom);
anchor_pos=dist+1;
anchor=n*n-(n-2*dist)*(n-2*dist)+1;
//cout<<dist<<" "<<anchor<<endl; //接下来进行模拟,可在4*inner_n-4次计算中找到目标(i,j)
int k=anchor_pos,m=anchor_pos; //当前位置
int direction=0; //当前方向
int top_bound=dist+1;
int bottom_bound=n-dist+1;
int left_bound=dist;
int right_bound=n-dist+1; int count=anchor; while(count<=n*n){
switch(direction){
case 0:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(m==right_bound){
//cout<<"reach right_bound "<<right_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
right_bound--;
m--;
k++;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
m++;
count++;
break; case 1:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(k==bottom_bound){
//cout<<"reach bottom_bound "<<bottom_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
bottom_bound--;
k--;
m--;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
k++;
count++;
break; case 2:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(m==left_bound){
//cout<<"reach left_bound "<<left_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
left_bound++;
m++;
k--;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
m--;
count++;
break; case 3:
//cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
if(k==top_bound){
//cout<<"reach top_bound "<<top_bound<<" change direction from "<<direction<<" ";
direction=(direction+1)%4;
top_bound++;
k++;
m++;
//cout<<"to "<<direction<<endl;
continue;
}
if(k==i&&m==j){
cout<<count<<endl;
return 0;
}
k--;
count++;
break;
} }
}
return 0;
}

3. 外圈+内圈优化,0(1)之路

1 2 3 4
12 12+1 12+2 5
11 12+4 12+3 6
10 9 8 7

 
 
1 2
4 3
 
从图中可以看出内圈减去外圈的最大值后,变成了最简单的一圈。
外圈的最大值记为anchor=n^2-inner_n^2。
通过观察规律可以发现
如果(i,j)在内圈的上部或右部,(i,j)在内圈的位置为inner_i+inner_j-1
如果在内圈的左部或下部,(i,j)在内圈的位置为4*inner_n-inner_i-inner_j-1
(i,j)在整个矩阵中的位置为外圈的最大值+内圈的位置。
该算法使得结果可以由公式直接计算出,它的时间复杂度为常数o(1)。
 
#include<iostream>
using namespace std; int n,i,j;
int anchor,dist;
int left,right,top,bottom; int min(int x,int y,int z,int q){
return min(min(min(x,y),z),q);
} int min(int x,int y){
return x>y?y:x;
} int f(){
int inner_n =n-2*dist;
int inner_i =i-dist;
int inner_j =j-dist;
//cout<<inner_n<<" "<<inner_i<<" "<<inner_j<<" "<<endl;
if(i-1==dist||n-j==dist){
return inner_i+inner_j-1;
}
return 4*inner_n-inner_i-inner_j-1;
} int main(){
cin>>n>>i>>j;
if(n==1){
cout<<1<<endl;
}else{
int left,right,top,bottom;
left=j-1;
right=n-j;
top=i-1;
bottom=n-i;
dist=min(left,right,top,bottom);
anchor=n*n-(n-2*dist)*(n-2*dist);
//cout<<dist<<" "<<anchor<<endl;
int ans = anchor+f();
cout<<ans<<endl;
}
return 0;
}

  

螺旋矩阵,两步进阶,从暴力到o(1)的更多相关文章

  1. 模拟【p2239】 螺旋矩阵

    顾z 你没有发现两个字里的blog都不一样嘛 qwq 题目描述--->p2239 螺旋矩阵 看到题,很明显,如果直接模拟的话,复杂度为\(O(n^2)\)过不去.(这个复杂度应该不正确,我不会分 ...

  2. 第29题:LeetCode54:Spiral Matrix螺旋矩阵

    给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素. 示例 1: 输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ...

  3. PAT 1050. 螺旋矩阵(25)

    本题要求将给定的N个正整数按非递增的顺序,填入"螺旋矩阵".所谓"螺旋矩阵",是指从左上角第1个格子开始,按顺时针螺旋方向填充.要求矩阵的规模为m行n列,满足条 ...

  4. NOIP2014-普及组复赛-第三题-螺旋矩阵

    题目描述 Description 一个n行n列的螺旋矩阵可由如下方法生成: 从矩阵的左上角(第1行第1列)出发,初始时向右移动:如果前方是未曾经过的格子,则继续前进,否则右转:重复上述操作直至经过矩阵 ...

  5. Code Kata:螺旋矩阵 javascript实现

    1 2 3 4  5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9  如图所示,就是一个5*5的螺旋矩阵 我的思路如下: 第一步:拆分 ...

  6. HrbustOJ 1564 螺旋矩阵

    Description 对于给定的一个数n,要你打印n*n的螺旋矩阵. 比如n=3时,输出: 1 2 3 8 9 4 7 6 5 Input 多组测试数据,每个测试数据包含一个整数n(1<=n& ...

  7. 使用 JS 输出螺旋矩阵

    关于螺旋矩阵 这是我曾经遇到过的面试题,在 LeetCode 上找到了题目的原型,难度中等.题目描述如下: 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中 ...

  8. PHP实现螺旋矩阵(螺旋数组)

    今天碰到一个比较有意思的问题, 就是把A到Y这25个字母以下面的形式输出出来 A B C D E P Q R S F O X Y T G N W V U H M L K J I 问题很有意思,就是转圈 ...

  9. 【模拟】[NOIP2014]螺旋矩阵[c++]

    题目描述 一个n行n列的螺旋矩阵可由如下方法生成: 从矩阵的左上角(第1行第1列)出发,初始时向右移动:如果前方是未曾经过的格子,则继续前进,否则右转:重复上述操作直至经过矩阵中所有格子.根据经过顺序 ...

随机推荐

  1. redis安全配置

    禁用或者重命名危险命令 FLUSHDB, FLUSHALL, KEYS, PEXPIRE, DEL, CONFIG, SHUTDOWN, BGREWRITEAOF, BGSAVE, SAVE, SPO ...

  2. 我的Android Studio配置

    Android Studio固然好用,但是刚从Eclipse转移到Android Studio上难免有很多不便,现在集中整理一下AS与Eclipse的区别,使得AS更好用. 快捷键Keymap到Ecl ...

  3. 基于Jquery的文本提示控件 poshytip

    Html中,如设置了title的属性,则当鼠标在该对象上面短暂的停留时,会显示预设的文本提示,但,这些效果只会短暂的显示,一会就会消失,又要重新把鼠标移出再移回来才被显示,样式也无法重写,实在是恼人之 ...

  4. 练习十八:求这样的一组数据和,s=a+aa+aaa+aaaa+aa...a,其中a为一个数字

    例如:2+22+222+2222+22222(此时共有5个数字相加),这里具体几个数字由键盘控制 方法一:普通做法 a = int(input("计算要加的数(1-9之间数):") ...

  5. Spring Boot 测试 junit

    import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.ann ...

  6. 这个五月,我拿到了腾讯暑期offer

    前情回顾 本来学校就是双非,如果再没有些亮点,是很难进大厂的. 我比较幸运,曾在网易实习过一段时间,很感谢这段经历,让我有幸通过腾讯HR们的简历筛选. 对于腾讯春招,其实我是没有参加网申(2.28-3 ...

  7. Maven的学习资料收集--(九) 构建SSH项目以及专栏maven

    在这里整合一下,使用Maven构建一个SSH项目 1.新建一个Web项目 可以参照前面的博客 2.添加依赖,修改pom.xml <project xmlns="http://maven ...

  8. 说说C#中的enum吧

    enum,就是枚举类型,它是struct,int,single,double一样,都属于值类型,从ValueType类型中派生,存储在栈中.它在被创建时,不需要分配内在空间,所以对程序的性能是有好处的 ...

  9. 【踩坑】服务器部署springboot应用时报错--端口被tomcat占用

    今天将本机尬聊一下项目(基于netty-socketio)的服务端程序调试好以后,通过jar包部署在服务器的时候,出现了报错,提示tomcat已经占用了端口. 之前在部署iReview项目时的确是通过 ...

  10. 《Head First 设计模式》之装饰者模式——饮料加工

    装饰者模式(Decorator) ——动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 特点:建立拥有共同超类的装饰者与被装饰者来实现功能的动态扩展 原则:对扩展开放,对 ...