今天收到畅游的在线笔试,就打算来试试了,就当去校招前练练手,因为畅游校招只招19届…ORZ
畅游的题很奇怪,分为两大类,第一类与游戏相关的基础题和阅读理解(中间涉及一些小计算),第二类就是编程相关的单选题以及编程大题。
不讨论基础题,只是论编程题的话,难度简单,然鹅我还是沙比了,犯了很多错误,编程答题有两道大题,第一题是计算最短路径距离,第二题则是一个数值计算题。
编程大题1:
这题也是我犯傻的地方,在看到第一题的简介的时候(什么玩家鸭NPC鸭),因为题没看全,我第一时间想到的是ASTAR算法,因为考虑到可能存在障碍物所以才打算采用,于是乎我就开始写了一大段代码,以及回想ASTAR算法距离计算以及更新路径信息,然后每个函数调试一下,到最后看到测试用例以及说明就傻眼了,没有障碍物设置,就给了玩家坐标和NPC坐标,让你算和哪个NPC最近,再加上牛客和VS使用的编译器以及函数库有些不一样,导致有些文件显示未声明,浪费了很多时间,最后删了一大段代码,实际就解析字符串,计算距离就完事了-_-…
int setNpcPos(char *pos,vector &m_vec) {
int x, y, count = 0;
char *tmp = pos, *target;
char buff[10];
bool is = true;
while (tmp) {
target=strstr(tmp, ",");
if (target!=NULL) {
if (*target == '\0') {
break;
}
*target = '\0';
sprintf(buff, "%s", tmp);
int t_tmp = atoi(buff);
if (is) {
x = t_tmp;
is = false;
m_vec.push_back(x);
}
else {
y = t_tmp;
is = true;
count++;
m_vec.push_back(y);
}
*target = ',';
tmp = target+1;
}
else {
y = atoi(tmp);
count++;
m_vec.push_back(y);
break;
}
}
return count;
}
void findNpc(size_t x, size_t y, size_t npcnum, char *npcpos) {
int count = 0;
vector m_vec;
int tmax = 128, maxx=0, maxy=0;
//setPlayPos(x, y);
count=setNpcPos(npcpos,m_vec);
if (count != npcnum)
return;
for (int i = 0; i < 2*npcnum; i+=2) {
int tx = m_vec[i] - x;
int ty = m_vec[i + 1] - y;
int tsum = pow(tx, 2) + pow(ty, 2);
int tlen = sqrt(tsum);
if (tlen < tmax) {
tmax = tlen;
maxx = m_vec[i], maxy = m_vec[i+1];
}
}
cout << "(" << maxx << "," << maxy<<")";
}
编程大题2:
由于做到这的时候只剩10来分钟(时间都浪费在第一题了…),因为时间很少,脑子有些犯浑了,第一时间打算分析其规律,很显然,这是一个离散的数列(不知道可不可以这样形容),与其瞎找规律,还不如按照题目说的去实现,于是我就把给出的例子想成如下步骤:
1、当n=0时,返回1。
2、n>0时,设有一个列表,{0}与序列{0},将元素从1到k添加进入列表,每添加一个进入列表相应的就把该元素添加进入序列,然后再把添加的元素与集合中的每个元素的合添加进入序列,也就达到了题目的效果。
由于时间不够,这道题我没有完成,所以与面试无缘了,本身作为18届也没机会去面试=_=
PS:由于忘记截图测试用例,我也不知道自己的代码对不对,只是测试了一下题目默认给的序列,目前是正确的。
代码:
m_pow是为了解决重复计算pow,m_vec是存放序列,代码写得很烂,这也是我目前暂时想到得方法。
vector m_vec;
vector m_pow;
int calsum(int num,int n) {
static int flag = 1;
if (n == 0) {
return 1;
}
if (n == 1) {
return num;
}
int sum = 0;
int val;
if (flag < m_pow.size())
val = m_pow[flag+1];
else {
val = pow(num, flag);
}
m_vec.push_back(val);
m_pow.push_back(val);
flag++;
for (int i = 0; i+1 < flag; i++) {
int v = 0;
if (i < m_pow.size()) {
v = m_pow[i];
}
else {
v = pow(num, i);
m_pow.push_back(v);
}
int tmp = v + val;
sum += v;
m_vec.push_back(tmp);
}
if (flag != 2) {
sum += val;
m_vec.push_back(sum);
}
if (m_vec.size() >= n) {
return m_vec[n - 1];
}
return calsum(num, n);
}
int main() {
m_vec.push_back(1);
m_pow.push_back(1);
int sum = calsum(4, 10);
cout << sum << endl;
for (auto i : m_vec) {
cout << i << " ";
}
return 0;
}
单选题
就写一些自己迷惑的题吧,记不太清题目了。。。
1、
int ary[]={1,2,3};
int *p=ary;
int ret=*p+*p+++*++p;
在第一眼看到这个题,想起了网上说起的谭浩强C,虽然我没看过,但是也听说过,也知道这样的写法很不友好,但是我并没有记下来,到底是从右边向左边运算呢,还是分析运算符优先级什么的呢?我有点方了。
答案是6,虽然我第一时间也是选6,但是我犯浑了,后面又改成5,5是从左往右边运算的过程orz,我觉得这种要分析的话,从汇编上看比较好…
下面是语句的汇编代码,首先把p地址给eax,eax+4字节,然后再把eax地址返回给p,也就是p++之后的操作就不解释了,直到00A08255执行完后,也就是把值给ret后,再对P执行++语句,所以可以看出++p与p++的区别了,也知道其执行顺序。
int ret=*p+*p+++*++p;
00A0823D mov eax,dword ptr [p]
00A08240 add eax,4
00A08243 mov dword ptr [p],eax
00A08246 mov ecx,dword ptr [p]
00A08249 mov edx,dword ptr [ecx]
00A0824B mov eax,dword ptr [p]
00A0824E add edx,dword ptr [eax]
00A08250 mov ecx,dword ptr [p]
00A08253 add edx,dword ptr [ecx]
00A08255 mov dword ptr [ret],edx
00A08258 mov edx,dword ptr [p]
00A0825B add edx,4
00A0825E mov dword ptr [p],edx
2、
很久没写类继承方面,忘了很多,当我看到这个的时候,犹豫了,我知道派生类继承后,会存有一个类似于A的副本,毕竟要调用基类的构造函数,但是我这时候就纠结了,到底派生类中是否存在一个基类的对象,而这个对象是否会调用析构函数,忘干净了,于是乎就选了只输出类B的析构函数。
class A {
public:
~A() {
cout << "this a\n";
}
};
class B :public A {
public:
~B() {
cout << "this B";
}
};
int main() {
B t;
return 0;
}
答案是先输出B的,再输出A的,看看汇编就知道了(这两个月没白学…虽然拖得时间长了些…)
再main函数中只显示call B::B,没看到A!没关系逐语句调试进去看,就会发现除了调用B,之后还会调用A,再详细看就是下下面得汇编代码,也可以看到C++中的class大概布局,执行完B后缩栈,然后再调用A,似乎他们就是同步的,而其原因是其构造顺序决定的。
B::~B:
01011A05 jmp B::~B (01015FC0h)
A::~A:
01011A0A jmp A::~A (01015CC0h)
class B :public A {
~B() {
01015FC0 push ebp
01015FC1 mov ebp,esp
01015FC3 push 0FFFFFFFFh
01015FC5 push 1024610h
01015FCA mov eax,dword ptr fs:[00000000h]
01015FD0 push eax
01015FD1 sub esp,0CCh
01015FD7 push ebx
01015FD8 push esi
01015FD9 push edi
01015FDA push ecx
01015FDB lea edi,[ebp-0D8h]
01015FE1 mov ecx,33h
01015FE6 mov eax,0CCCCCCCCh
01015FEB rep stos dword ptr es:[edi]
01015FED pop ecx
01015FEE mov eax,dword ptr [__security_cookie (0102D014h)]
01015FF3 xor eax,ebp
01015FF5 push eax
01015FF6 lea eax,[ebp-0Ch]
01015FF9 mov dword ptr fs:[00000000h],eax
01015FFF mov dword ptr [this],ecx
01016002 mov ecx,offset _749050E7_hello.cpp (0103000Dh)
01016007 call @__CheckForDebuggerJustMyCode@4 (010115C3h)
cout << "this B";
0101600C push offset string "this B" (01028C8Ch)
01016011 mov eax,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0102E0CCh)]
01016016 push eax
01016017 call std::operator<< > (010114A1h)
0101601C add esp,8
}
0101601F mov ecx,dword ptr [this]
01016022 call A::~A (01011A0Ah)
01016027 mov ecx,dword ptr [ebp-0Ch]
0101602A mov dword ptr fs:[0],ecx
01016031 pop ecx
01016032 pop edi
01016033 pop esi
01016034 pop ebx
01016035 add esp,0D8h
0101603B cmp ebp,esp
0101603D call __RTC_CheckEsp (010115FFh)
}
3、
继承关系,这里我忘记了一件事,私有继承的话,所有成员都不可见,这里忘了,的确不太应该,该好好回想C++了…emmmmmm
4、
这个我好像没选错,但是值得记一下…
class A {
public:
int a;
A(int val = 0) {
a = val;
}
friend void operator++(A &m) {
m.a++;
}
};
关于重载,好像记得题目给得不清,让答题者选友元函数是怎么调用的。
因为++是单目运算符,一个参数即可,但这是前置++的写法(++A),题目好像没说明是后置还是前置++,还是我忘了= =
如果后置的话,需要添加下面这种,有点类似于用友元函数重载输入输出运算符(二目),就可以使用A++了,相当于++从左边传入参数_
friend void operator++(A &m,int) {
m.a++;
}
5、
好像没有印象深刻的题了,想起在补充吧。