阿里巴巴实习笔试:摸金校尉

Posted 禾小白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里巴巴实习笔试:摸金校尉相关的知识,希望对你有一定的参考价值。

原题是:

一个摸金校尉要通过一个矩形墓室,入口的位置为(0,0),出口的位置为(m,n)。墓室中散布着一些激光发射器,某些激光发射器之间有激光。m,n和激光的起始和终止位置(x1,y1,x2,y2)均为整数。请问他能否不碰到激光,成功通过墓室。

 

思路:与激光器相碰就是激光器所在的线段与摸金校尉走的下一步的线段相交;在每一个点处,都有上下左右四个可行的方向,所以只要写一个深搜递归就可以找到一条通向出口的路径,下面附上代码,因为是直接在网页上面写的,没有编译过,所以可能存在错误,欢迎交流

#include<iostream>
#include<vector> using namespace std; int m , n ; //出口位置 int num; //墓室里面激光线段的个数; struct point { double x,y; }; struct segment { point begin,end; }; double min(double x,double y) { return x<y?x:y; } double max(double x,double y) { return x>y?x:y; } bool onsegment(point pi,point pj,point pk) //判断点pk是否在线段pi pj上 { if(min(pi.x,pj.x)<=pk.x&&pk.x<=max(pi.x,pj.x)) { if(min(pi.y,pj.y)<=pk.y&&pk.y<=max(pi.y,pj.y)) { return true; } } return false; } double direction(point pi,point pj,point pk) //计算向量pkpi和向量pjpi的叉积 { return (pi.x-pk.x)*(pi.y-pj.y)-(pi.y-pk.y)*(pi.x-pj.x); } bool judge(point p1,point p2,point p3,point p4) //判断线段p1p2和p3p4是否相交,相交的话就代表沿当前方向会与激光器触碰,返回true { double d1 = direction(p3,p4,p1); double d2 = direction(p3,p4,p2); double d3 = direction(p1,p2,p3); double d4 = direction(p1,p2,p4); if(d1*d2<0&&d3*d4<0) return true; if(d1==0&&onsegment(p3,p4,p1)) return true; if(d2==0&&onsegment(p3,p4,p2)) return true; if(d3==0&&onsegment(p1,p2,p3)) return true; if(d4==0&&onsegment(p1,p2,p4)) return true; return false; } /*在每一个节点处都有四个可能的方向,上下左右,如果当前没有找到解,并且上下左右任一方向的下一个点没有经历的话,就进行搜索,直到搜索到一个解为止*/ bool findways( int x , int y , vector<vector<bool> > &isthrough ){ // 递归找路径 bool ret = false; isthrough[x][y] = true; //标记当前点已经历 if( x == m && y == n ) //如果到达终点的话返回true return true; else{ //在每一个点有上下左右四个方向可以走 point start ; //记录当前点的位置 start.x = x; start.y = y; if( y + 1 <= n && isthrough[x][y + 1] && !ret ){ //如果向上的那个点没有搜索到,就在墓室的范围内沿y轴向上走 , !ret是当前没有找到路径 point end; //记录搜索的下一个顶点的位置 end.x = x; end.y = y + 1; bool temp = true; //临时变量,用于记录是否与某一激光线段相交 for(int i = 0 ; i < num ; ++i ){ //遍历所有激光线段,判断是否相交 if( judge( start , end , seg[i].begin , seg[i].end ) ){ temp = false; break; //如果有一条相交的话,就记录并退出 } } if(temp){ //如果没有与任何激光线段相交的话,就向下一个点搜索 ret = findways( x , y + 1 , isthrough ); } } if( y - 1 >= 0 && isthrough[x][y - 1] && !ret ){ //如果向下的那个点没有搜索到,就在墓室的范围内沿y轴向下走 , !ret是当前没有找到路径 point end; end.x = x; end.y = y - 1; bool temp = true; for( int i = 0 ; i < num ; ++i ){ if( judge( start , end , seg[i].begin , seg[i].end ) ){ temp = false; break; } } if(temp){ //如果没有记录到相交的话 ret = findways( x , y - 1 , isthrough ); } } if(x + 1 <= m && isthrough[x + 1][y] && !ret ){ //如果向右的那个点没有搜索到,就在墓室的范围内沿x轴向右走 , !ret是当前没有找到路径 point end; end.x = x + 1; end.y = y; bool temp = true; for(int i = 0 ; i < num ; ++i ){ if(judge(start ,end ,seg[i].begin , seg[i].end)){ temp = false; break; } } if(temp){ //如果没有记录到相交的话 ret = findways( x + 1 , y , isthrough ); } } if(x - 1 >= 0 && isthrough[x - 1][y] && !ret ){ //如果向左的那个点没有搜索到,就在墓室的范围内沿x轴向左走, !ret是当前没有找到路径 point end; end.x = x - 1; end.y = y; bool temp = true; for(int i = 0 ; i < num ; ++i ){ if(judge(start ,end ,seg[i].begin , seg[i].end)){ temp = false; break; } } if(temp){ //如果没有记录到相交的话 ret = findways( x - 1 , y , isthrough ); } } } isthrough[x][y] = false; //标记当前点已经历 return ret; } int main(){ //读图 cin >> num; segment *seg = new segment[num]; //new一个num个激光线段结构的数组 for(int i = 0 ; i < n ; ++i ){ cin>>seg[i].begin.x>>seg[i].begin.y>>seg[i].end.x>>seg[i].end.y; //输入激光线段 } cin >> m >> n; //输入出口位置 vector<vector<bool> > isthrough(m + 1, vector<bool>(n + 1 , false)); //记录该点是否经历,避免产生回路;初始化为false ,因为包含0行0列,所以 + 1 bool ret = findways(0 , 0 , isthrough); if(ret){ cout<<"成功通过!"; } else{ cout<<"不能通过!"; } return 0; }

  

以上是关于阿里巴巴实习笔试:摸金校尉的主要内容,如果未能解决你的问题,请参考以下文章

阿里新零售4大技术部2018联合招募实习生~内推免笔试!

阿里前端笔试总结

盗墓笔记—阿里旺旺ActiveX控件imageMan.dll栈溢出漏洞研究

阿里巴巴暑期实习面试-算法岗位(机器学习)2018-03-28

百度2016笔试(算法春招实习)

微众银行2020前端实习生笔试总结