Chinainvent的专栏

---樽前作剧莫相笑,死后诸君思我狂

用户操作
[即时聊天] [发私信] [加为好友]
张云开ID:chinainvent
76739次访问,排名1383(-1),好友10人,关注者10人。
追梦少年
chinainvent的文章
原创 99 篇
翻译 0 篇
转载 14 篇
评论 140 篇
Chinainvent的公告

习惯了一个人的早晨,
一个人开始新的一天,
没有不确定,
没有无奈,
因为,
没有期待...
习惯了一个人上路,
没有方向,
没有归期,
只想找一个属于自己的地方,
停留...
最近评论
lkm;:nk;ljbkyggggggio
你们搞啥呢:
int demo(int x, int y)
{
int array[4][4] = {
{ 7, 8, 9, 10},
{ 6, 1, 2, 11},
{ 5, 4, 3, 12},
{16, 15, 14, 13}
……
liulunlendy:没这么复杂吧,为什么不只动一个呢?
路过:最后那个terminal不要选vt100啊,选成linux就可以了。
bluehouse1985:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
文章分类
收藏
相册
2006春节
中南校园
我的链接
absurd
acm题库
Amin Saberi
Android
Android开发者论坛
book
Boulevard
Cocoal17
Connie
g9yuayon
Google论文集
hacker.com
http://www.javvin.net/WirelessTerms/m.php
K歌之王
lambda-the-ultimate
programming.reddit.com
qzone
TheFreeDictionayr
tywfei
ubuntu中文论坛
云风
公益电子书
周国平
李敖
电子图书
章亦春
韩寒
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 一道网易笔试题(加入最优解法)收藏

新一篇: 数据结构复习篇:内排序 | 旧一篇: 第一份个人简历

最近看完了表、栈、队列、二叉树、二叉搜索树、堆、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、9构成)的边长
    
//的一半为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)为内层的结点数。
    
//例如,结点10的内层由结点1和正方形A(2、3、4、5、7、8、10)构成
    
//这些内层的总结点数恰为:(正方形A的边长+1)的平方,
    
//因为:正方形A的边长 =(结点10所在正方形的半径-1)*2
    
//故:内层结点数 = (结点10所在正方形的边长-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;
}

发表于 @ 2006年10月13日 04:31:00|评论(loading...)|编辑

新一篇: 数据结构复习篇:内排序 | 旧一篇: 第一份个人简历

评论

#alula 发表于2006-10-13 11:58:00  IP: 219.134.241.*
是python语言, 不常用?
[code]
#! /usr/bin/env python

def f(x, y):
w = max(abs(x), abs(y))
x += w
y += w
w *= 2

if y == 0:
s = w * 3 + x
elif x == 0:
s = w * 2 + (w - y)
elif y == w:
s = w + (w - x)
else:
s = y
return s + (w - 1) ** 2

for x, y in [(0,0), (0,1), (1,2), (-1,-2), (8, 9)]:
print (x, y), f(x, y)

[/code]
(0, 0) 1
(0, 1) 4
(1, 2) 14
(-1, -2) 22
(8, 9) 308
#Hello 发表于2006-10-13 08:39:00  IP: 61.52.154.*
能否这样解决呢?搞一个2维数组,分别存储数字和坐标,然后分别对应即可。岂不是更简单吗?
#chinainvent 发表于2006-10-13 12:05:00  IP: 222.240.65.*
T0 alula帅哥:干吗不留下你的博客链接呢。
#brucesea 发表于2006-10-13 11:02:00  IP: 210.94.41.*
是呀,你当然可以预先计算出每个结点的值,放在一个二位数组里。
输入x,y坐标,查询数组就行了,不用每次都计算的。
#chinainvent 发表于2006-10-13 12:10:00  IP: 222.240.65.*
希望大家有优秀的解法,贴上来,最好花一点时间,加上必要的注释。好让大家更易理解你们良好的思维方法。
#comstep 发表于2006-10-13 12:13:00  IP: 221.222.180.*
对于坐标是正数的:
x>y的:
((x-1)*2 + 1)^2 + x + y
x<=y的:
(y*2)^2 - (x + y) + 1

负数的还没弄明白

不知道正确否?
#alula 发表于2006-10-13 11:35:00  IP: 219.134.241.*
def f(x, y):
w = max(abs(x), abs(y))
x += w
y += w
w *= 2

if y == 0:
s = w * 3 + x
elif x == 0:
s = w * 2 + (w - y)
elif y == w:
s = w + (w - x)
else:
s = y
return s + (w - 1) ** 2

for x, y in [(0,0), (0,1), (1,2), (-1,-2), (8, 9)]:
print (x, y), f(x, y)
#chinainvent 发表于2006-10-13 11:47:00  IP: 222.240.65.*
楼上这位帅哥,用的是什么语言,python?没用过,希望楼下有简洁解法的朋友,用常用的语言写出来。大家一起学习。
#chinainvent 发表于2006-10-13 09:16:00  IP: 222.240.65.*
这也确实是一种方法,但,假如我输入一个很大的坐标(1000,-1000),你能预先知道它的结点值吗?网易出这道题的目的,应该在于考查我们构建算法的能力。
#bennie 发表于2006-10-13 13:27:00  IP: 61.138.15.*
可以直接计算的,以数字1为零点,做一条斜率为1的直线,在这条直线下方的都是从(2n-1)^2 + 1到(2n)^2的数,其中n为max(x,y)。而在直线上方(包括直线上)都是从(2n)^2 + 1到(2n+1)^2。n的概念同上。那么很轻易就可以算出坐标点的数,然后有条件的加或减另一个坐标量就可以了。
#bennie 发表于2006-10-13 16:49:00  IP: 61.138.15.*
数学原理观察一下可以得出,每一段数(2n-1, 2n+1]构成了一层边(以1位中心的矩形边框),根据max就可以得出边的开始number,然后那四个条件是判断坐标处于哪条边上,然后用边的中点数加上另一个坐标(min)就可以了。
==该死的csdn blog
#bennie 发表于2006-10-13 14:13:00  IP: 61.138.15.*
是max(|x|,|y|)。
刚看了一下那个python代码,不用分成直线上下了,是把从两个奇数的平方之间作为一个线段,然后根据坐标情况判断一下得到基于线段的偏移。
#chinainvent 发表于2006-10-13 15:57:00  IP: 220.169.2.*
我刚才,我把alula的python程序,改成c++的格式,运行结果是正确无误的:
int f(int x, int y)
{
int w = max(abs(x),abs(y));
x += w;
y += w;
w *= 2;

int s;
if (y == 0)
s = 3*w + x;
else if (x == 0)
s = 2*w + (w - y);
else if (y == w)
s = w + (w - x);
else
s = y;
return s + pow((w-1),2);
}
这表明alula对这个问题的认识,非常深刻,已经是数学层面了。但,我还是没明白你们的推导过程。这可能是一个经典问题,希望楼下的作一个详细的推导,最好指出这个问题的本质。
#alula 发表于2006-10-15 09:13:00  IP: 219.133.117.*
to LZ:
Python代码的解问题思路并不复杂,
将问题转化为求一个内部正方形中包含的点的个数与外部正方形的边上的点的个数.
设外部正方形的边长为U(=代码中w*2+1), 则内部正方形内包含的点数为: (U-2)^2,外部正方形边上的点个数求法为代码中的s。


简单解释一下代码:

int w = max(abs(x),abs(y));
x += w;
y += w;
w *= 2;
///上面这几句做了坐标变换,便于随后的算术运算。


int s;
if (y == 0)
s = 3*w + x;
else if (x == 0)
s = 2*w + (w - y);
else if (y == w)
s = w + (w - x);
else
s = y;
///上面这几句求外部边上的点个数s。

return s + pow((w-1),2);
///答案为s + 内部正方形内点的个数。
#VirtualBox 发表于2006-10-22 17:18:00  IP: 222.240.107.*
呵呵,我也来插上一个,也是基值加偏移的方式,不过模型和上面的不一样:
==================================
##Emacs
##按设计的包裹模型,1是第一层,2,3,4为包裹的第二层,5-9是包裹的第三层,依此类推
##[/Code]
def getNumber(x,y):
n = max(abs(x),abs(y)) ## 得到投影距离

frame = 0
onTheLeft = 0
if (x+y)>0: ##注意包裹模型的分界直线y=-x,如果在直线下方,那么frame=2*n
frame = 2*n
else: ##如果在直线上方,那么frame=2*n-1
frame = 2*n + 1
onTheLeft = 1 ##设置好在直线上方的标志,下面要用到

if frame==0:
frame = 1 ##处理(0,0)的情况
cordOffset = (frame-1)/2

x += cordOffset ##调整坐标方便下面的偏移计算
y += cordOffset

offset = 0

if onTheLeft: ##以下几行计算偏移,分别处理在四条框上的情况
if x==0:
offset = frame-y
elif y==0:
offset = frame+x
else:
print "Should never print this!"
else:
if x==frame-1:
offset = y + 1
elif y==frame-1:
offset = frame + (frame - x - 1)
else:
print "Should never print this!"

value = (frame-1)**2 + offset ##计算总值,上一个包裹层最后的值加上本层的偏移

return value

for x,y in [(0,0),(1,0),(0,1),(-1,-1),(-1,2),(-1,-2),(8,9)]:
print "(%d,%d):%d"%(x,y,getNumber(x,y))



##[/Result]
# (0,0):1
# (1,0):2
# (0,1):4
# (-1,-1):7
# (-1,2):16
# (-1,-2):22
# (8,9):308
#VirtualBox 发表于2006-10-22 17:20:00  IP: 222.240.107.*
晕,居然把python里需要的空格/Tab全部去除了~无法阅读了~
#LLBSY 发表于2007-10-19 00:17:41  IP: 222.178.10.*
经典啊
#游客 发表于2007-12-21 12:12:15  IP: 60.12.235.*
受你启发,我发现有个规律,那就是每走两个方向后,其步数就加一,我用Java实现了一下,代码要近一百行,就不在这献丑了,呵呵。
#哈哈 发表于2008-06-03 22:24:57  IP: 123.122.55.*
这个题目??
这不就是一个二维数组吗?
只是原点坐标偏移了1。
将上面的矩阵存成一个二维数组 Array

输入x,y 坐标

print Array[x+1][y+1];

解决!相信任何语言都会支持次方法吧?
#你们搞啥呢 发表于2008-11-21 01:54:55  IP: 218.10.4.*

int demo(int x, int y)
{
int array[4][4] = {
{ 7, 8, 9, 10},
{ 6, 1, 2, 11},
{ 5, 4, 3, 12},
{16, 15, 14, 13}
};

return array[y + 1][x + 1];
}
发表评论  


登录
Csdn Blog version 3.1a
Copyright © Chinainvent