蛇形数组和直接计算编号

先看两个示例:

这两个数组一个宽度是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个点)的情况。正如后添加注释中的内容,横纵坐标傻傻搞不清……

posted @ 2017-01-19 10:30  zcsor~流浪dè风  Views(387)  Comments(0Edit  收藏  举报