一道网易笔试题(加入最优解法)

最近看完了表、栈、队列、二叉树、二叉搜索树、堆、Huffuman树的数据结构。突然想试试自己的程序水平有没有提高(似乎有点太急于求成^_^)。恰逢自己刚给网易投了一份简历,于是,在百度上搜了一些网易的笔试题。结果,其中这道题,一下把我难住了:
 
如图:
 
 
 
设“1”的坐标为(0,0) “7”的坐标为(-1,-1) 编写一个小程序,使程序做到输入坐标(X,Y)之后显示出相应的数字。
 
这个图有点像螺旋,它是按顺时针方向(由1->2->3->4->5…),逐渐向外膨胀。显然,如果以1为原点(0,0),以向右为x轴的正方向,以向下为y轴的正方向:
那么用人眼很容易就看出7的坐标为(-1,-1),这正与题目的假设一致。其他,如5的坐标应为(-1,1)。
但是,怎么让计算机知道呢?我的大脑突然不知所措,我在想,如果真出了这道题我挂定了^_^。但是,既然现在我是安然的坐在自己的卧室里,我绝对不会向它投降。
于是,躺在床上,望着天花板,想象着从这个螺旋的原点1出发,一起来看看:
Step1:从1向左走1步到达2。坐标(0,0)->(1,0),即x加1。
Step2:从2向下走1步到达3,坐标(0,1)->(1,1),即y加1。
Step3:从3向右走1步到达4,坐标(1,1)->(0,1),即x减1。
Step4:从4向右走1步到达5,坐标(0,1)->(-1,1),即x减1。
Step5:从5向上走1步到达6,坐标(-1,1)->(-1,0),即y减1。
Step6:从6向上走1步到达7,坐标(-1,0)->(-1,-1),即y减1。
……
当在大脑中想象着这一步一步的异同,一个灵感闪灵了。你是否发现了其中的规律?好吧,让我总结一下它的规律:
1、每向左(left)走一步,x就加1。每向右(right)走一步,x就减1。每向下(down)走一步,y就加1。每向上(up)走一步,y就减1。
这一条规律,只要有点直角坐标的知识,是显而易见的。当我们用程序去模拟这样一步一步地行走的时候,另外一个问题,就浮出水面了:怎样告诉计算机,让它按照一个顺时针的螺旋方式,去遍历每一个结点?比如,当计算机来到结点2的时候,它怎么知道,下一步应该往下走呢?当计算机来到结点5的时候,它怎么知道,下一点应该往上走呢?其实,这里还藏着另一条规律:
我把它叫做 状态转换的规律。这个规律包含两个方面:
2.1、从结点1开始,
A、向右走
B、向下走
C、向左走
D、向上走
又回到A。形成一个循环,即:A->B->C->D->A->B……。
在每一个方向上,应该走多少步,才改变方向呢?现假设,现在开始改变方向,且已知上一次向右走了rNum步,向下走了dNum步,向左走了lNum步,向上走了uNum步。那么从现在开始,
2.2、沿新方向所走的步数,应该等于上一次相反方向所走的步数加1。例如,现在处于结点3,刚才从2->3的时候是往下走,现在要改变方向,向左走了。这个时候,由于上一次向右走了1步(即由1->2)。所以,这次应该向左走1+1=2步。
 
找到以上的两个规律,就可以写程序了。
我们可以用枚举类型,来标识四个方向(right,down,left,up)。
用四个整形变量,来记录上一次各个方向所走的步数(rNum,dNum,lNum,uNum)。
用四个整形变量,来记录在每一个方向累积所走的步数(rSum,dSum,lSum,uSum)。
这样,结点值就由这个公式确定:nodeVal = lSum+dSum+rSum+uSum+1。之所以加1,是因为原点的值为1,而不是0。
而坐标值可以这样确定:因为向右为x轴正方向,故x = rSum-lSum。因为向下为y轴的正方向,故y = dSum-uSum。
以下,是我用C++写的程序:
#include  < iostream >
#include 
< conio.h >
using   namespace  std;

// 输入坐标,返回结点值
int  Val( int  x,  int  y)
{
    
if  (x == 0   &&  y == 0 )
        
return   1 ;
    
// 四种行走方向
     enum  Order{right, down, left, up};
    
// 上一次的行走的方向
    
// 从状态循环思考,显然由1到2的上一步的状态应该是up。
    Order oState  =  up;    
    
    
// 上一次 右、下、左、上移动的次数
    
// 皆初始化为0
     int  rNum = 0 , dNum = 0 , lNum = 0 , uNum = 0 ;

    
// 左、下、右、上累积移动的次数, 
    
// 坐标满足:因为向右为x轴正方向,故x=rSum-lSum;
    
// 因为向下为y轴正方向,故y=dSum-uSum;
    
// 坐标所在结点的数值为:lSum+rSum+uSum+dSum+1;
    
// 皆初始化为0
     int  lSum = 0 , dSum = 0 , rSum = 0 , uSum = 0 ;

    
// 一个无限循环,一旦找到与x、y相等的坐标,则结束。
     while  ( true )
    {
        
// 状态转换
         switch  (oState)
        {
        
case  up:     // up->right        
            {
                rNum 
=   0 ;
                
for  ( int  i = 0 ; i < lNum + 1 ; i ++ )
                {
                    
++ rNum;
                    
++ rSum;
                    
if  (x  ==  rSum - lSum  &&  y  ==  dSum - uSum)
                        
return  (lSum + rSum + uSum + dSum + 1 );

                }
                oState 
=  right;
                
break ;
            }
        
case  right:     // right->down
            {
                dNum 
=   0 ;
                
for  ( int  i = 0 ; i < uNum + 1 ; i ++ )
                {
                    
++ dNum;
                    
++ dSum;
                    
if  (x  ==  rSum - lSum  &&  y  ==  dSum - uSum)
                        
return  (lSum + rSum + uSum + dSum + 1 );
                }
                oState 
=  down;
                
break ;
            }
        
case  down:     // down->left
            {
                lNum 
=   0 ;
                
for  ( int  i = 0 ; i < rNum + 1 ; i ++ )
                {
                    
++ lNum;
                    
++ lSum;
                    
if  (x  ==  rSum - lSum  &&  y  ==  dSum - uSum)
                        
return  (lSum + rSum + uSum + dSum + 1 );
                }
                oState 
=  left;
                
break ;
            }
        
case  left:     // left->up
            {
                uNum 
=   0 ;
                
for  ( int  i = 0 ; i < dNum + 1 ; i ++ )
                {
                    
++ uNum;
                    
++ uSum;
                    
if  (x  ==  rSum - lSum  &&  y  ==  dSum - uSum)
                        
return  (lSum + rSum + uSum + dSum + 1 );
                }
                oState 
=  up;
                
break ;
            }
        }
    }    

}

int  main( int  argc, char   *  argv[])
{
    
int  x, y;
    cout 
<< " 请输入坐标(x y): " ;
    
while  (cin >> x >> y)
    {
    cout 
<< " 坐标所在的结点值为: " <<  Val(x, y) << endl;
    cout 
<< " 请输入坐标(x y): " ;
    }
    
return   0 ;
}
  补充:此题最优秀的解法―――根据alula朋友的精彩回贴整理。能够根据规律挖掘出一个求解公式,就是最快速的方法。下面这个程序,做了充分的注释,只要在草稿上,按照注释,作一个图,就一目了然了:
#include  < iostream >
#include 
< conio.h >
#include 
< math.h >
using   namespace  std;

int  newVal( int  x,  int  y)
{
    
// 以结点1为原点
    
// 以相邻两结点间的距离为单位(如结点2与结点3的之间线段)
    
// 结点7所在的正方形(由结点2、3、4、5、6、7、8构成)的边长
    
// 的一半为1,即结点7到原点1的最大投影距离为1。
    
// 于是由结点坐标,可以求出此结点所在的正方形的投影距离:
     int  r  =  max(abs(x),abs(y));    
    
    
// 进行坐标变换,即把坐标原点移动到正方形的一个角结点上,
    
// 使整个正方形落在第一象限,例如,当r=1时,将把坐标原点从结点1
    
// 移动到结点7。
    x  +=  r;
    y 
+=  r;

    
// 正方形的边长,等于投影距离的两倍
     int  d  =   2 * r;

    
int  s;     // s为结点在自己的正方形的偏移量
     if  (y  ==   0 )
        s 
=   3 * +  x;
    
else   if  (x  ==   0 )
        s 
=   2 * +  (d - y);
    
else   if  (y  ==  d)
        s 
=  d  +  (d - x);
    
else  
        s 
=  y;

    
// pow((r+1),2)为内层的结点数。
    
// 例如,结点9的内层由结点1和正方形A(2、3、4、5、7、8)构成
    
// 这些内层的总结点数恰为:(正方形A的边长+1)的平方,
    
// 因为:正方形A的边长 =(结点9所在正方形的半径-1)*2
    
// 故:内层结点数 = (结点9所在正方形的边长-1)的平方
   //结点值 = 在当前正方形的偏移量 + 内层的结点数
    s  +=  pow((d - 1 ), 2 );

    
return  s;

}
int  main( int  argc, char   *  argv[])
{
    
int  x, y;
    cout 
<< " 请输入坐标(x y): " ;
    
while  (cin >> x >> y)
    {
    cout 
<< " 坐标所在的结点值为: " << f(x, y) << endl;
    cout 
<< " 请输入坐标(x y): " ;
    }
    
return   0 ;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值