畅游笔试题错题小记

  今天收到畅游的在线笔试,就打算来试试了,就当去校招前练练手,因为畅游校招只招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:
畅游笔试题错题小记_第1张图片
  由于做到这的时候只剩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、
好像没有印象深刻的题了,想起在补充吧。

你可能感兴趣的:(C++,面试与笔试)