螺旋矩阵 noip2014普及组
本题可以直接模拟填数字,也可以直接计算结果。
代码一:(这个代码,缺陷在于数组太大,浪费内存啊。另外,循环次数也不少。总之,时间空间的消耗都不小。)
1 /*=============================================================== 2 本段代码是模拟往数组填数字的过程。 3 每填写一个值就判断该位置是否(i,j)。若寻到目标位置, 4 则输出答案。 5 =================================================================*/ 6 #include<stdio.h> 7 int a[3001][3001]={0};//这个大小的静态数组尚可申请,但假如是放在函数内部声明却不行了的。这个大小大概是9MB,但距离30000*30000的大小实在是太远了。 8 int main() 9 { 10 int n,i,j; 11 int m;//m表示总共的层数 12 int k,p,q;//循环变量 13 int flag=0;//标志性变量:等于0表示尚未循环到目标元素(i,j) 14 int t; 15 int len; 16 17 scanf("%d%d%d",&n,&i,&j); 18 m=(n+1)/2; //m表示总共的层数 19 t=1; //t表示要填进数组的数字 20 for(k=1;k<=m&&flag==0;k++) 21 { 22 p=k,q=k; //(k,k)是第k层左上角坐标点 23 len=n-2*(k-1);//表示当前层中每一条边的元素个数 24 for(;q<=(k+len-1);q++)//填充当前层的顶边 25 { 26 a[p][q]=t; 27 if(p==i&&q==j) 28 { 29 printf("%d\n",a[p][q]); 30 return 0; 31 } 32 t++; 33 } 34 q--; 35 p++; 36 for(;p<=(k+len-1);p++)//填充当前层的右边 37 { 38 a[p][q]=t; 39 if(p==i&&q==j) 40 { 41 printf("%d\n",a[p][q]); 42 return 0; 43 } 44 t++; 45 } 46 p--; 47 q--; 48 for(;q>=k;q--)//填充当前层的下边 49 { 50 a[p][q]=t; 51 if(p==i&&q==j) 52 { 53 printf("%d\n",a[p][q]); 54 return 0; 55 } 56 t++; 57 } 58 q++; 59 p--; 60 for(;p>k;p--)//填充当前层的左边 61 { 62 a[p][q]=t; 63 if(p==i&&q==j) 64 { 65 printf("%d\n",a[p][q]); 66 return 0; 67 } 68 t++; 69 } 70 } 71 return 0; 72 }
代码二:(这个代码缺陷在于循环时间没减少……)
1 /*=============================================================== 2 本段代码也是模拟往数组填数字的过程。但没有申请数组内存来存储数据。 3 而是只用一个变量表示每一次要填写的数字。 4 毕竟题目只要输出(i,j)位置的值,故不用保存整个数组。 5 只需要判断每一次模拟到的位置是否是(i,j)即可。 6 =================================================================*/ 7 #include<stdio.h> 8 int main() 9 { 10 int n,i,j; 11 int m;//m表示总共的层数 12 int k,p,q;//循环变量 13 int flag=0;//标志性变量:等于0表示尚未循环到目标元素(i,j) 14 int t; 15 int len; 16 17 scanf("%d%d%d",&n,&i,&j); 18 m=(n+1)/2; //m表示总共的层数 19 t=1; //t表示要填进数组的数字 20 for(k=1;k<=m&&flag==0;k++) 21 { 22 p=k,q=k; //(k,k)是第k层左上角坐标点 23 len=n-2*(k-1);//表示当前层中每一条边的元素个数 24 for(;q<=(k+len-1);q++)//填充当前层的顶边 25 { 26 if(p==i&&q==j) 27 { 28 printf("%d\n",t); 29 return 0; 30 } 31 t++; 32 } 33 q--; 34 p++; 35 for(;p<=(k+len-1);p++)//填充当前层的右边 36 { 37 if(p==i&&q==j) 38 { 39 printf("%d\n",t); 40 return 0; 41 } 42 t++; 43 } 44 p--; 45 q--; 46 for(;q>=k;q--)//填充当前层的下边 47 { 48 if(p==i&&q==j) 49 { 50 printf("%d\n",t); 51 return 0; 52 } 53 t++; 54 } 55 q++; 56 p--; 57 for(;p>k;p--)//填充当前层的左边 58 { 59 if(p==i&&q==j) 60 { 61 printf("%d\n",t); 62 return 0; 63 } 64 t++; 65 } 66 } 67 return 0; 68 }
代码三:(这个代码应该算是比较完美了,时间空间都降下来了,而且是O(1)时间的算法。)
1 /*=============================================================== 2 本段代码不是模拟填写数字的过程,而是直接根据i和j的值计算(i,j)所在的层k。 3 然后计算第k层左上角(k,k)位置处的元素值。 4 接下来呢,要寻到 (i,j)处的值,可以有两种方法: 5 (1)模拟填数字的过程环绕一圈寻找(i,j)。 6 (2)根据i,j的值依次计算(k,k)距离(i,j)的几个段的距离即可得到答案。 7 这里使用第二个方案。 8 =================================================================*/ 9 #include<stdio.h> 10 int main() 11 { 12 int n,i,j; 13 int k,t; 14 freopen("matrix10.in","r",stdin); 15 freopen("matrix10.txt","w",stdout); 16 scanf("%d%d%d",&n,&i,&j); 17 //根据i判断(i,j)所在层数k 18 t=(n+1)/2; 19 if(i<=t) 20 { 21 if(j<=t) k=(i<=j?i:j);//(i,j)在左上部分 22 else k=(i<(n+1-j)?i:(n+1-j)); //(i,j)在右上部分 23 } 24 else 25 { 26 if(j<=t) k=(n+1-i)<j?(n+1-i):j; //(i,j)在左下部分 27 else k=(n+1-i)<(n+1-j)?(n+1-i):(n+1-j); //(i,j)在右下部分 28 } 29 //计算第k层左上角坐标为(k,k)的数t 30 k--; 31 t=n*n-(n-2*k)*(n-2*k)+1;//注意:n-2k表示剥去k层之后剩余部分的行数和列数.(i,j)所在层没被剥掉,故而要先执行k--。 32 k++; 33 if(i==k) //(i,j)在第k环的上边线 34 t=t+(j-k); 35 else if(j==k+n-2*(k-1)-1) //(i,j)在环的右边. 36 t=t+(n-2*(k-1)-1)+(i-k); 37 else if(i==k+n-2*(k-1)-1)//(i,j)在环的下边 38 t=t+(n-2*(k-1)-1)+(n-2*(k-1)-1)+((k+n-2*(k-1)-1)-j); 39 else t=t+(n-2*(k-1)-1)+(n-2*(k-1)-1)+(n-2*(k-1)-1)+((k+n-2*(k-1)-1)-i); 40 printf("%d\n",t); 41 return 0; 42 }
关于计算层数的代码:
其实是把整个数组分为左上、右上、左下和右下四个区域分别处理。
1 #include<stdio.h> 2 int main() 3 { 4 int n,i,j,k,t; 5 scanf("%d",&n); 6 for(i=1;i<=n;i++) 7 { 8 for(j=1;j<=n;j++) 9 { 10 t=(n+1)/2; 11 if(i<=t) 12 { 13 if(j<=t) k=(i<=j?i:j);//(i,j)在左上部分 14 else k=(i<(n+1-j)?i:(n+1-j)); //(i,j)在右上部分 15 } 16 else 17 { 18 if(j<=t) k=(n+1-i)<j?(n+1-i):j; //(i,j)在左下部分 19 else k=(n+1-i)<(n+1-j)?(n+1-i):(n+1-j); //(i,j)在右下部分 20 } 21 printf("%d ",k); 22 } 23 printf("\n"); 24 } 25 return 0; 26 }
关于计算(i,i)处的值:
n*n-(n-2*k)*(n-2*k)+1
其中,n*n是数字总的个数,k是表示被剥去的层数(注意:假如(i,j)在第k层,则被剥去的是(k-1)层。)
(n-2*k)*(n-2*k)表示剥去之后,剩余部分的元素的个数。
如何理解n-2*k呢?其实啊,就是“每剥去一层,行和列的数量都会减少2,于是总共减少的数量就是2*k。剩余的部分自然就是n-2*k了。”