您的位置:首页 > 编程语言

2017阿里实习生招聘编程题之墓室

2017-04-27 14:36 399 查看
题目描述: 一个摸金校尉要通过一个矩形墓室,入口的位置为(0,0),出口位置为(m,n)。墓室中散步着一些散光发射器,某些激光发射器之间有激光。m,n和激光的起始和终止位置(x1,y1,x2,y2)均为整数。请问他能否不碰到激光,成功通过墓室。



题目分析: 首先通过深度优先遍历(DFS)根据直线是否相交确定所有的激光线段所构成的连通分量,例如,右图有3个连通分量,分别记录每个连通分量的横坐标的最小值和最大值,纵坐标的最小值和最大值。 那么当有连通分量横坐标最小最大值分别为0和m, 或纵坐标最小最大值为0和n, 或横、纵坐标最小值都为m ,或横、纵坐标的最大值分别为m和n时,不能通过墓室,这样求很容易写出程序了。

#include<iostream>
#include<algorithm>
#include<vector>
int cnt = 1;   //cnt定义图的连通分量的个数, 第一个连通分量(cnt=1),第2个(cnt=2),依次类推
struct Point   //point为定义的坐标(x,y)
{
int x;
int y;
};
struct lt   //存放每个连通分量的 横,纵坐标的最小最大值
{
int x_min;    //横坐标最小值
int x_max;
int y_min;
int y_max;
};
struct lt liantong[100];   //存放每个连通分量的4个最大最小坐标值
int visited[100] = { 0 };  //标记该条线是否被访问,  线之间是否相连的连通分量的个数
using namespace std;

double mult(Point a, Point b, Point c)  //相当于求斜率   K(bc)-K(ba)
{
return (a.x - c.x)*(b.y - c.y)
4000
- (b.x - c.x)*(a.y - c.y);
}
//aa, bb为一条线段两端点 cc, dd为另一条线段的两端点 相交返回true, 不相交返回false
bool intersect(Point aa, Point bb, Point cc, Point dd)   //判断2条直线是否相交
{
if (max(aa.x, bb.x)<min(cc.x, dd.x))
{
return false;
}
if (max(aa.y, bb.y)<min(cc.y, dd.y))
{
return false;
}
if (max(cc.x, dd.x)<min(aa.x, bb.x))
{
return false;
}
if (max(cc.y, dd.y)<min(aa.y, bb.y))
{
return false;
}
if (mult(cc, bb, aa)*mult(bb, dd, aa)<0)
{
return false;
}
if (mult(aa, dd, cc)*mult(dd, bb, cc)<0)
{
return false;
}
return true;
}

void DFS(vector<Point>& vec1, vector<Point>& vec2, int k, int visited[100])    //深度优先确定连通分量的个数
{
visited[k] = cnt;
for (int i = 0; i<vec1.size(); i++)
{
if (visited[i] == 0)  //如果还没有被访问过
{
if (intersect(vec1[k], vec2[k], vec1[i], vec2[i]))  //如果k和i 这2条直线连通
DFS(vec1, vec2, i,visited);
}
}
}
int main()
{
int i, j, m, n, k;
cin >> m >> n;
cout << "请输入激光的条数:" << endl;
cin >> k;
cout << "请输入k个激光的坐标,每条线有4个点,分别为坐标(x1,y1),(x2,y2):" << endl;
vector<Point> vec1, vec2;
for (i = 0; i<k; i++)
{
Point point1, point2;
cin >> point1.x >> point1.y >> point2.x >> point2.y;
vec1.push_back(point1);
vec2.push_back(point2);
}
//  int *visited = new int[k];  //标记该条线是否被访问,  线之间是否相连的连通分量的个数
//  memset(visited, 0, sizeof(visited));
for (i = 0; i<k; i++)
{
if (visited[i] == 0)
{
DFS(vec1, vec2, i, visited);
liantong[cnt].x_min = min(vec1[i].x, vec2[i].x);
liantong[cnt].x_max = max(vec1[i].x, vec2[i].x);
liantong[cnt].y_min = min(vec1[i].y, vec2[i].y);
liantong[cnt].y_min = max(vec1[i].y, vec2[i].y);
for (j = 0; j<k; j++)
{
if (visited[j] == cnt) //如果该线段属于第cnt个连通分量
{
if (min(vec1[j].x, vec2[j].x)<liantong[cnt].x_min)
liantong[cnt].x_min = min(vec1[j].x, vec2[j].x);

if (max(vec1[j].x, vec2[j].x)>liantong[cnt].x_max)
liantong[cnt].x_max = max(vec1[j].x, vec2[j].x);

if (min(vec1[j].y, vec2[j].y)<liantong[cnt].y_min)
liantong[cnt].y_min = min(vec1[j].y, vec2[j].y);

if (max(vec1[j].y, vec2[j].y)>liantong[cnt].y_max)
liantong[cnt].y_max = max(vec1[j].y, vec2[j].y);
}
}
cnt++;
}
}
bool flag = false;
for (i = 1; i <= cnt; i++)  //一共cnt个连通分量
{              //直线连通分量把密室完全挡住了
if ((liantong[i].x_min == 0 && liantong[i].x_max == m) || (liantong[i].y_min == 0 && liantong[i].y_max == n)|| (liantong[i].x_min == 0 && liantong[i].y_min == 0) || (liantong[i].x_max == m && liantong[i].y_max == n))
{
cout << "不能成功通过密室" << endl;
flag = true;
break;
}
}
if (!flag)
cout << "可以通过密室" << endl;
system("pause");
return 0;
}


输入:



由于时间仓促,代码可能有些bug或考虑不全的地方,欢迎各位大佬给出建议。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: