您的位置:首页 > 理论基础 > 数据结构算法

中国大学MOOC-数据结构基础习题集、06-1、Saving James Bond - Hard Version

2015-01-09 23:00 525 查看
题目链接:http://www.patest.cn/contests/mooc-ds/06-1

题目分析:这是一道考察图的广度优先遍历,同时也要借助于Dijstra算法的一道题。题目的背景与上周的05-2是相同的:007被困在一个孤岛上,孤岛的直径是15,池塘的范围是[±50, ±50]。池塘中鳄鱼的条数及坐标,007的跳跃半径通过输入给出。问007能否借助于池塘中的鳄鱼逃出生天?和上周要求不同的是,需要把最短路径长度及最短路径输出,在有多条最短路径的情况下,输出第一跳最近的那条。

特别说明:

  1. 是否建立图随意,用若干个数组表示也可以。博主最开始是没有建的,但是这次贴的代码是建好图的(邻接表)。一方面是为了练习自己的编码能力,一方面也是给对建图不熟悉的同学们举个“栗子”。感兴趣的同学可以关注下103~158行。

  2. 题目中要求“多条最短路径输出第一跳最近的那条”,博主查找了许多别的资料,最后使用的优先级队列。使用时需要重载小于运算符(即operator<)。将在孤岛上能踩到的所有鳄鱼结点,以距孤岛中心的距离为优先级,放入优先级队列中。然后依次取出,做广度优先遍历。这部分在165~180+行。

  3. 在做广度优先遍历时,如果已经可以逃脱,要确定当前是否为最短路径,是则记录路径。可以关注一下198~215行。

  4. 求两点间距离的函数,要判断其中一点是不是“岛屿”,如果是的话,结果要加上孤岛的半径。函数的定义在63~78行。

  5. 如果Case4过不去的朋友,请试一下我的用例1。如果是Case5过不去的朋友,请试一我的用例2。

建议测试如下数据: 

  4 20 -27 0 -41 0 0 26 0 40

  博主这里输出结果是:3 0 26 0 40

  1 42.5

  博主这里输出结果是:1

代码分析:

  注释写的非常详细,大家可以阅读一下:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>

using namespace std;

/*
* 所用结构体的声明
* pos 坐标结构体
* vexNode 邻接表中的结点
*/
struct pos;
template <class T> struct vexNode;

/*
* 宏定义的声明
* FIRSTSTEP 小岛的半径,固定为7.5
* BORDER 边界的大小,固定为50
* MAXNUM 无穷大
*/

#define FIRSTSTEP 7.5
#define BORDER 50
#define MAXNUM 100000000

/*
* 全局变量的声明
* vec 存储鳄鱼的坐标
* eVec 邻接表存储图
* pathVec 用来存储路径
*/

vector<pos> vec;
vector<vexNode<int> > eVec;
vector<int> pathVec;

struct pos
{
double x;
double y;
pos(double a, double b):x(a),y(b) {}
};

template <class T>
struct vexNode
{
T data;
vexNode<T> *next;
vexNode(T d, vexNode<T> *n = NULL):data(d), next(n) {}
bool friend operator<(const vexNode &a, const vexNode &b)
{
int V = a.data;
int W = b.data;
int dV = vec[V].x * vec[V].x + vec[V].y * vec[V].y;
int dW = vec[W].x * vec[W].x + vec[W].y * vec[W].y;
return dV < dW; // 出队先出大的,再出小的
}
};

/*
* 计算两点之间的距离
*/
double Distance(pos p1, pos p2, int dis)
{
double xx = (p1.x - p2.x) * (p1.x - p2.x);
double yy = (p1.y - p2.y) * (p1.y - p2.y);
if((p1.x == 0 && p1.y == 0) || (p2.x == 0 && p2.y == 0))
{
return dis + FIRSTSTEP - sqrt(xx + yy);
}
else
{
return dis - sqrt(xx + yy);
}
}

/*
* 获得路径
*/
vector<int> getPath(int t, int p[])
{
vector<int> path;
for(; t!=-1; t=p[t])
path.push_back(t);
reverse(path.begin(),path.end());
return path;
}

int main()
{
int nNum;
double dis;
cin >> nNum >> dis;
// 考虑特殊情况,能否一步迈出
if(dis + FIRSTSTEP >= BORDER)
{
cout << "1" << endl;
return 0;
}
// 起始点(小岛)也算一个点
vec.push_back(pos(0, 0));
eVec.push_back(vexNode<int>(0));
nNum ++;
// 用邻接表存储图
for(int i=1; i<nNum; i++)
{
double a, b;
cin >> a >> b;
vec.push_back(pos(a,b));
eVec.push_back(vexNode<int>(i));
}
// 开始建图
for(int i=0; i<nNum; i++)
{
for(int j=0; j<nNum; j++)
{
if(i != j)
{
if(Distance(vec[i], vec[j], dis) >= 0)
{
// 查一查有没有重复的
bool myIFlag = false;
vexNode<int> *p = &eVec[i];
while(p -> next != NULL)
{
p = p -> next;
if(p -> data == j)
{
myIFlag = true;
break;
}
}
// 如果没有重复的,就插在最后边
if(myIFlag == false)
p -> next = new vexNode<int>(j);

// 因为是无向图,也就是双向图,所以另一侧也要插
bool myJFlag = false;
vexNode<int> *q = &eVec[j];
while(q -> next != NULL)
{
q = q -> next;
if(q -> data == i)
{
myJFlag = true;
break;
}
}
// 如果没有重复的,就插在最后边
if(myJFlag == false)
q -> next = new vexNode<int>(i);
}
}
}
}
// 相关数据结构的申请
int *dist = new int[nNum];
int *path = new int[nNum];
priority_queue<vexNode<int> > myQueue;

// 算法开始
// 1. 在相同的最短路里找第一步最小的放入优先级队列中

vexNode<int> *p = &eVec[0];
while(p -> next != NULL)
{
p = p -> next;
myQueue.push(eVec[p->data]);
path[p->data] = 0;
}
int flag = 1;   // flag用来标记是否是第一次
int minDist;    // minDist记录最小的dist值

// 2. 从岛屿开始,能到达的所有结点做循环

while(!myQueue.empty())
{
// 2.1 初始化
for(int i=0; i<nNum; i++)
{
dist[i] = -1;
path[i] = -1;
}
// 2.2 从队列中弹出一个结点,从这个结点开始,借助另一个队列,进行BFS
vexNode<int> vN = myQueue.top();
myQueue.pop();
path[vN.data] = 0;      // 从myQueue队列中取出的结点,parent一定为岛屿(0,0)
queue<int> bfsQueue;    // 进行BFS所需要的队列
bfsQueue.push(vN.data);
dist[vN.data] = 0;      // 初始的dist值为0
while(!bfsQueue.empty())
{
int W = bfsQueue.front();
bfsQueue.pop();
// 2.3 判定是不是已经可以上岸了
if(fabs(vec[W].x-BORDER)<=dis || fabs(vec[W].x+BORDER)<=dis
||fabs(vec[W].y-BORDER)<=dis ||(vec[W].y+BORDER)<=dis)
{
// 2.3.1 如果是第一次,更新minDist值,并记录路径
if(flag&&W!=0)
{
minDist = dist[W];
flag = 0;
pathVec = getPath(W, path);
}
// 2.3.2 如果不是第一次,则比较minDist值与dist值,并更新路径
else if(W!=0 && dist[W] <= minDist)
{
minDist = dist[W];
pathVec = getPath(W, path);
}
}
// 2.4 如果没有上岸,则将其邻接结点放入队列中,并更新dist与path的值
else
{
for(int i=1; i<=nNum; i++)
{
if(Distance(vec[W], vec[i], dis) >= 0 && dist[i] == -1)
{
bfsQueue.push(i);
dist[i]=dist[W]+1;
path[i]=W;
}
}
}

}
}

// 3. 输出最终结果

if(pathVec.size() == 0)
cout << "0" << endl;
else
{
// 3.1 因为我们把(0,0)也当成结点了,这里不用+1
cout << pathVec.size() << endl;
for(int i=0; i<pathVec.size(); i++)
// 3.2 因为我们把(0,0)也当成结点了,但是不能让它输出,所以特殊考虑
if(vec[pathVec[i]].x == 0 and vec[pathVec[i]].y == 0)
;
else
cout << vec[pathVec[i]].x << " " << vec[pathVec[i]].y << endl;
}
return 0;
}


AC成果:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐