题目描述

一个 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 列的数是多少。

输入描述:

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

输出描述:

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

输入

  1. 4 2 3

输出

  1. 14

备注:

  1. 对于 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%的数据。

  1. #include<iostream>
  2. using namespace std;
  3.  
  4. /***********************
  5. 1<=n<=30000无法用数组存储
  6. ***********************/
  7.  
  8. int n,i,j;
  9. int top_bound,bottom_bound,left_bound,right_bound;
  10. int count=1;
  11. int direction=0;
  12.  
  13. int main(){
  14. cin>>n>>i>>j;
  15. int k=1,m=1;
  16. top_bound=1;
  17. bottom_bound=n+1;
  18. left_bound=0;
  19. right_bound=n+1;
  20. while(count<=n*n){
  21. switch(direction){
  22. case 0:
  23. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  24. if(m==right_bound){
  25. //cout<<"reach right_bound "<<right_bound<<" change direction from "<<direction<<" ";
  26. direction=(direction+1)%4;
  27. right_bound--;
  28. m--;
  29. k++;
  30. //cout<<"to "<<direction<<endl;
  31. continue;
  32. }
  33. if(k==i&&m==j){
  34. cout<<count<<endl;
  35. return 0;
  36. }
  37. m++;
  38. count++;
  39. break;
  40.  
  41. case 1:
  42. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  43. if(k==bottom_bound){
  44. //cout<<"reach bottom_bound "<<bottom_bound<<" change direction from "<<direction<<" ";
  45. direction=(direction+1)%4;
  46. bottom_bound--;
  47. k--;
  48. m--;
  49. //cout<<"to "<<direction<<endl;
  50. continue;
  51. }
  52. if(k==i&&m==j){
  53. cout<<count<<endl;
  54. return 0;
  55. }
  56. k++;
  57. count++;
  58. break;
  59.  
  60. case 2:
  61. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  62. if(m==left_bound){
  63. //cout<<"reach left_bound "<<left_bound<<" change direction from "<<direction<<" ";
  64. direction=(direction+1)%4;
  65. left_bound++;
  66. m++;
  67. k--;
  68. //cout<<"to "<<direction<<endl;
  69. continue;
  70. }
  71. if(k==i&&m==j){
  72. cout<<count<<endl;
  73. return 0;
  74. }
  75. m--;
  76. count++;
  77. break;
  78.  
  79. case 3:
  80. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  81. if(k==top_bound){
  82. //cout<<"reach top_bound "<<top_bound<<" change direction from "<<direction<<" ";
  83. direction=(direction+1)%4;
  84. top_bound++;
  85. k++;
  86. m++;
  87. //cout<<"to "<<direction<<endl;
  88. continue;
  89. }
  90. if(k==i&&m==j){
  91. cout<<count<<endl;
  92. return 0;
  93. }
  94. k--;
  95. count++;
  96. break;
  97. }
  98.  
  99. }
  100. return 0;
  101. }

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)
  1. #include<iostream>
  2. using namespace std;
  3.  
  4. /***************************
  5. 1. 1 ≤ n ≤ 30000,因此无法将整个矩阵存储下来,只能输出目标结果
  6. 2. 模拟整个过程仍会超时,可将情况分为3类
  7. (1)n==1, 输出1
  8. (2)i,j==1|n,即该数在旋转矩阵的外围计算旋转矩阵边界上的值
  9. (3)中间的数,根据首先计算外围数的个数,在外圈的数的基础上进行模拟
  10. ****************************/
  11. int n,i,j;
  12. int anchor=1,dist,anchor_pos=1;
  13.  
  14. int min(int x,int y,int z,int q){
  15. return min(min(min(x,y),z),q);
  16. }
  17. int min(int x,int y){
  18. return x>y?y:x;
  19. }
  20.  
  21. int main(){
  22. cin>>n>>i>>j;
  23. if(n==1){
  24. cout<<1<<endl;
  25. }else{
  26. int left,right,top,bottom;
  27. left=j-1;
  28. right=n-j;
  29. top=i-1;
  30. bottom=n-i;
  31. //cout<<left<<" "<<right<<" "<<top<<" "<<bottom<<endl;
  32. dist=min(left,right,top,bottom);
  33. anchor_pos=dist+1;
  34. anchor=n*n-(n-2*dist)*(n-2*dist)+1;
  35. //cout<<dist<<" "<<anchor<<endl;
  36.  
  37. //接下来进行模拟,可在4*inner_n-4次计算中找到目标(i,j)
  38. int k=anchor_pos,m=anchor_pos; //当前位置
  39. int direction=0; //当前方向
  40. int top_bound=dist+1;
  41. int bottom_bound=n-dist+1;
  42. int left_bound=dist;
  43. int right_bound=n-dist+1;
  44.  
  45. int count=anchor;
  46.  
  47. while(count<=n*n){
  48. switch(direction){
  49. case 0:
  50. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  51. if(m==right_bound){
  52. //cout<<"reach right_bound "<<right_bound<<" change direction from "<<direction<<" ";
  53. direction=(direction+1)%4;
  54. right_bound--;
  55. m--;
  56. k++;
  57. //cout<<"to "<<direction<<endl;
  58. continue;
  59. }
  60. if(k==i&&m==j){
  61. cout<<count<<endl;
  62. return 0;
  63. }
  64. m++;
  65. count++;
  66. break;
  67.  
  68. case 1:
  69. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  70. if(k==bottom_bound){
  71. //cout<<"reach bottom_bound "<<bottom_bound<<" change direction from "<<direction<<" ";
  72. direction=(direction+1)%4;
  73. bottom_bound--;
  74. k--;
  75. m--;
  76. //cout<<"to "<<direction<<endl;
  77. continue;
  78. }
  79. if(k==i&&m==j){
  80. cout<<count<<endl;
  81. return 0;
  82. }
  83. k++;
  84. count++;
  85. break;
  86.  
  87. case 2:
  88. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  89. if(m==left_bound){
  90. //cout<<"reach left_bound "<<left_bound<<" change direction from "<<direction<<" ";
  91. direction=(direction+1)%4;
  92. left_bound++;
  93. m++;
  94. k--;
  95. //cout<<"to "<<direction<<endl;
  96. continue;
  97. }
  98. if(k==i&&m==j){
  99. cout<<count<<endl;
  100. return 0;
  101. }
  102. m--;
  103. count++;
  104. break;
  105.  
  106. case 3:
  107. //cout<<"from "<<k<<" "<<m<<" steps to "<<"direction "<<direction<<endl;
  108. if(k==top_bound){
  109. //cout<<"reach top_bound "<<top_bound<<" change direction from "<<direction<<" ";
  110. direction=(direction+1)%4;
  111. top_bound++;
  112. k++;
  113. m++;
  114. //cout<<"to "<<direction<<endl;
  115. continue;
  116. }
  117. if(k==i&&m==j){
  118. cout<<count<<endl;
  119. return 0;
  120. }
  121. k--;
  122. count++;
  123. break;
  124. }
  125.  
  126. }
  127. }
  128. return 0;
  129. }

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)。
 
  1. #include<iostream>
  2. using namespace std;
  3.  
  4. int n,i,j;
  5. int anchor,dist;
  6. int left,right,top,bottom;
  7.  
  8. int min(int x,int y,int z,int q){
  9. return min(min(min(x,y),z),q);
  10. }
  11.  
  12. int min(int x,int y){
  13. return x>y?y:x;
  14. }
  15.  
  16. int f(){
  17. int inner_n =n-2*dist;
  18. int inner_i =i-dist;
  19. int inner_j =j-dist;
  20. //cout<<inner_n<<" "<<inner_i<<" "<<inner_j<<" "<<endl;
  21. if(i-1==dist||n-j==dist){
  22. return inner_i+inner_j-1;
  23. }
  24. return 4*inner_n-inner_i-inner_j-1;
  25. }
  26.  
  27. int main(){
  28. cin>>n>>i>>j;
  29. if(n==1){
  30. cout<<1<<endl;
  31. }else{
  32. int left,right,top,bottom;
  33. left=j-1;
  34. right=n-j;
  35. top=i-1;
  36. bottom=n-i;
  37. dist=min(left,right,top,bottom);
  38. anchor=n*n-(n-2*dist)*(n-2*dist);
  39. //cout<<dist<<" "<<anchor<<endl;
  40. int ans = anchor+f();
  41. cout<<ans<<endl;
  42. }
  43. return 0;
  44. }

  

螺旋矩阵,两步进阶,从暴力到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. css奇技淫巧—border-radius

    官方介绍: 浏览器支持:IE9+, Firefox 4+, Chrome, Safari 5+,和Opera支持border-radius属性. border-radius 属性是一个最多可指定四个 ...

  2. CCF201809(Java)

    第一题: 问题描述 在一条街上有n个卖菜的商店,按1至n的顺序排成一排,这些商店都卖一种蔬菜. 第一天,每个商店都自己定了一个价格.店主们希望自己的菜价和其他商店的一致,第二天,每一家商店都会根据他自 ...

  3. 【分享】利用WMITool解决浏览器主页被hao123劫持问题

    我在别处发的帖子 http://www.52pojie.cn/thread-607115-1-1.html

  4. python_sting字符串的方法及注释

    string类型是python内置的类型,无需安装   方法/属性 说明   capitalize()   把字符串的第一个字符改为大写   casefold()   把整个字符串的所有字符改为小写 ...

  5. tomcat的webapps下放置多个项目时会出现很多exception

    今天干了一件比较逗比的事,在tomcat的项目目录wepapps下又新建了一个文件夹backup,然后在backup下放置了之前项目的war包...然后启动tomcat的时候各种exception 大 ...

  6. Gym 100886J Sockets 二分答案 + 贪心

    Description standard input/outputStatements Valera has only one electrical socket in his flat. He al ...

  7. Java local 转UTC时间

    import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...

  8. codesmith 在安装32位Oracle 客户端问题解决

    问题解决办法如下: https://blog.csdn.net/csdn1152789046/article/details/52248669

  9. python协程与异步协程

    在前面几个博客中我们一一对应解决了消费者消费的速度跟不上生产者,浪费我们大量的时间去等待的问题,在这里,针对业务逻辑比较耗时间的问题,我们还有除了多进程之外更优的解决方式,那就是协程和异步协程.在引入 ...

  10. Android 悬浮窗权限各机型各系统适配大全

    这篇博客主要介绍的是 Android 主流各种机型和各种版本的悬浮窗权限适配,但是由于碎片化的问题,所以在适配方面也无法做到完全的主流机型适配,这个需要大家的一起努力,这个博客的名字永远都是一个将来时 ...