先看两个示例:
这两个数组一个宽度是5,一个宽度是4。以左图为例如果要在数组里面蛇形输出,从1开始x递增,到达5时改为y递增,到达9时x递减,13时y递减(注意此时的miny)完成一圈进入内圈。这样就可以在数组里面输出这些数字。在NOI上做了一个小兔子捡金币的问题,如果在M*M的矩阵内,每个点有一个金币,小兔子按以下规则从左上角开始捡金币:前方没有金币时右转,前方有金币时继续向前,问小兔子达到(X,Y)时有多少个金币。这就是一个典型的蛇形访问数组的问题,只不过这个问题不一定要逐个访问,可以根据X,Y计算出来。
观察左右两图,每一圈被划分为4个段:红色字体-蓝色字体-绿色字体-黑色字体。左图和右图有一点不同:在圈缩小过程中,内圈比外圈宽度少2,左图初始宽度为奇数,退化的最终结果是一个点。所以,我们可以知道,每一圈的元素数为:4*(len-1)。可以使用从外向内遍历的方式判断(X,Y)是否在一圈上,也可以直接根据(X,Y)判断在哪一圈。后者实现时需要先计算一个数组:记录经过N个外圈时编号行进了多少;前者边计算边记录这个编号偏移量,而且编码比较简单。所以,采用逐圈判定的方法即可:
#include<iostream> #include<cstring> using namespace std; struct point{ int x; int y; point(){ this->x=0; this->y=0; } point(int x,int y){ this->x=x; this->y=y; } }; int idxinrect(point lt,point rd,point p,int curlen){ if(p.y==lt.y && p.x<rd.x){ //上边 return p.x-lt.x+1; }else if(p.x==rd.x && p.y<rd.y){ //右边 return curlen+p.y-lt.y+1; }else if(p.y==rd.y && p.x>lt.x){ //下边 return curlen*2+rd.x-p.x+1; }else if(p.x==lt.x && p.y>lt.y){ //左边 return curlen*3+rd.y-p.y+1; } return -1; } int search(point p,int size){ int lastid,curlen,id; point lt=point(1,1),rd=point(size,size); lastid=0; curlen=size-1; while(curlen>0){ id=idxinrect(lt,rd,p,curlen); //在这一圈上的时候返回在这一圈内的偏移,否则返回-1。 if(id!=-1){ return lastid+id; } lastid+=curlen*4; //记录这一圈的长度 lt.x++;lt.y++; //缩小到下一圈 rd.x--;rd.y--; curlen-=2; } if(curlen==0){ //0时退化为一个点。就是中间一点,其编号为size*size。 return ++lastid; } return -1; } int main() { int i,k,n; point p; cin>>k>>n; for(i=0;i<k;i++){ cin>>p.x>>p.y; //若用于解题,互换此处的.x.y cout<<search(p,n)<<endl; } }
代码中单独处理了初始外圈长度为奇数(最内圈为1个点)的情况。正如后添加注释中的内容,横纵坐标傻傻搞不清……