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

题目链接: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

代码分析:

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

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <vector>
  4 #include <cmath>
  5 #include <queue>
  6 #include <stack>
  7 
  8 using namespace std;
  9 
 10 /*
 11  * 所用结构体的声明
 12  * pos 坐标结构体
 13  * vexNode 邻接表中的结点
 14  */
 15 struct pos;
 16 template <class T> struct vexNode;
 17 
 18 /*
 19  * 宏定义的声明
 20  * FIRSTSTEP 小岛的半径,固定为7.5
 21  * BORDER 边界的大小,固定为50
 22  * MAXNUM 无穷大
 23  */
 24 
 25 #define FIRSTSTEP 7.5
 26 #define BORDER 50
 27 #define MAXNUM 100000000
 28 
 29 /*
 30  * 全局变量的声明
 31  * vec 存储鳄鱼的坐标
 32  * eVec 邻接表存储图
 33  * pathVec 用来存储路径
 34  */
 35 
 36 vector<pos> vec;
 37 vector<vexNode<int> > eVec;
 38 vector<int> pathVec;
 39 
 40 struct pos
 41 {
 42     double x;
 43     double y;
 44     pos(double a, double b):x(a),y(b) {}
 45 };
 46 
 47 template <class T>
 48 struct vexNode
 49 {
 50     T data;
 51     vexNode<T> *next;
 52     vexNode(T d, vexNode<T> *n = NULL):data(d), next(n) {}
 53     bool friend operator<(const vexNode &a, const vexNode &b)
 54     {
 55         int V = a.data;
 56         int W = b.data;
 57         int dV = vec[V].x * vec[V].x + vec[V].y * vec[V].y;
 58         int dW = vec[W].x * vec[W].x + vec[W].y * vec[W].y;
 59         return dV < dW; // 出队先出大的,再出小的
 60     }
 61 };
 62 
 63 /*
 64  * 计算两点之间的距离
 65  */
 66 double Distance(pos p1, pos p2, int dis)
 67 {
 68     double xx = (p1.x - p2.x) * (p1.x - p2.x);
 69     double yy = (p1.y - p2.y) * (p1.y - p2.y);
 70     if((p1.x == 0 && p1.y == 0) || (p2.x == 0 && p2.y == 0))
 71     {
 72         return dis + FIRSTSTEP - sqrt(xx + yy);
 73     }
 74     else
 75     {
 76         return dis - sqrt(xx + yy);
 77     }
 78 }
 79 
 80 /*
 81  * 获得路径
 82  */
 83 vector<int> getPath(int t, int p[])
 84 {
 85     vector<int> path;
 86     for(; t!=-1; t=p[t])
 87         path.push_back(t);
 88     reverse(path.begin(),path.end());
 89     return path;
 90 }
 91 
 92 int main()
 93 {
 94     int nNum;
 95     double dis;
 96     cin >> nNum >> dis;
 97     // 考虑特殊情况,能否一步迈出
 98     if(dis + FIRSTSTEP >= BORDER)
 99     {
100         cout << "1" << endl;
101         return 0;
102     }
103     // 起始点(小岛)也算一个点
104     vec.push_back(pos(0, 0));
105     eVec.push_back(vexNode<int>(0));
106     nNum ++;
107     // 用邻接表存储图
108     for(int i=1; i<nNum; i++)
109     {
110         double a, b;
111         cin >> a >> b;
112         vec.push_back(pos(a,b));
113         eVec.push_back(vexNode<int>(i));
114     }
115     // 开始建图
116     for(int i=0; i<nNum; i++)
117     {
118         for(int j=0; j<nNum; j++)
119         {
120             if(i != j)
121             {
122                 if(Distance(vec[i], vec[j], dis) >= 0)
123                 {
124                     // 查一查有没有重复的
125                     bool myIFlag = false;
126                     vexNode<int> *p = &eVec[i];
127                     while(p -> next != NULL)
128                     {
129                         p = p -> next;
130                         if(p -> data == j)
131                         {
132                             myIFlag = true;
133                             break;
134                         }
135                     }
136                     // 如果没有重复的,就插在最后边
137                     if(myIFlag == false)
138                         p -> next = new vexNode<int>(j);
139 
140                     // 因为是无向图,也就是双向图,所以另一侧也要插
141                     bool myJFlag = false;
142                     vexNode<int> *q = &eVec[j];
143                     while(q -> next != NULL)
144                     {
145                         q = q -> next;
146                         if(q -> data == i)
147                         {
148                             myJFlag = true;
149                             break;
150                         }
151                     }
152                     // 如果没有重复的,就插在最后边
153                     if(myJFlag == false)
154                         q -> next = new vexNode<int>(i);
155                 }
156             }
157         }
158     }
159     // 相关数据结构的申请
160     int *dist = new int[nNum];
161     int *path = new int[nNum];
162     priority_queue<vexNode<int> > myQueue;
163 
164     // 算法开始
165     // 1. 在相同的最短路里找第一步最小的放入优先级队列中
166 
167     vexNode<int> *p = &eVec[0];
168     while(p -> next != NULL)
169     {
170         p = p -> next;
171         myQueue.push(eVec[p->data]);
172         path[p->data] = 0;
173     }
174     int flag = 1;   // flag用来标记是否是第一次
175     int minDist;    // minDist记录最小的dist值
176 
177     // 2. 从岛屿开始,能到达的所有结点做循环
178 
179     while(!myQueue.empty())
180     {
181         // 2.1 初始化
182         for(int i=0; i<nNum; i++)
183         {
184             dist[i] = -1;
185             path[i] = -1;
186         }
187         // 2.2 从队列中弹出一个结点,从这个结点开始,借助另一个队列,进行BFS
188         vexNode<int> vN = myQueue.top();
189         myQueue.pop();
190         path[vN.data] = 0;      // 从myQueue队列中取出的结点,parent一定为岛屿(0,0)
191         queue<int> bfsQueue;    // 进行BFS所需要的队列
192         bfsQueue.push(vN.data);
193         dist[vN.data] = 0;      // 初始的dist值为0
194         while(!bfsQueue.empty())
195         {
196             int W = bfsQueue.front();
197             bfsQueue.pop();
198             // 2.3 判定是不是已经可以上岸了
199             if(fabs(vec[W].x-BORDER)<=dis || fabs(vec[W].x+BORDER)<=dis
200                     ||fabs(vec[W].y-BORDER)<=dis ||(vec[W].y+BORDER)<=dis)
201             {
202                 // 2.3.1 如果是第一次,更新minDist值,并记录路径
203                 if(flag&&W!=0)
204                 {
205                     minDist = dist[W];
206                     flag = 0;
207                     pathVec = getPath(W, path);
208                 }
209                 // 2.3.2 如果不是第一次,则比较minDist值与dist值,并更新路径
210                 else if(W!=0 && dist[W] <= minDist)
211                 {
212                     minDist = dist[W];
213                     pathVec = getPath(W, path);
214                 }
215             }
216             // 2.4 如果没有上岸,则将其邻接结点放入队列中,并更新dist与path的值
217             else
218             {
219                 for(int i=1; i<=nNum; i++)
220                 {
221                     if(Distance(vec[W], vec[i], dis) >= 0 && dist[i] == -1)
222                     {
223                         bfsQueue.push(i);
224                         dist[i]=dist[W]+1;
225                         path[i]=W;
226                     }
227                 }
228             }
229 
230         }
231     }
232 
233     // 3. 输出最终结果
234 
235     if(pathVec.size() == 0)
236         cout << "0" << endl;
237     else
238     {
239         // 3.1 因为我们把(0,0)也当成结点了,这里不用+1
240         cout << pathVec.size() << endl;
241         for(int i=0; i<pathVec.size(); i++)
242             // 3.2 因为我们把(0,0)也当成结点了,但是不能让它输出,所以特殊考虑
243             if(vec[pathVec[i]].x == 0 and vec[pathVec[i]].y == 0)
244                 ;
245             else
246                 cout << vec[pathVec[i]].x << " " << vec[pathVec[i]].y << endl;
247     }
248     return 0; 
249 }

AC成果:

posted @ 2015-01-09 23:00  聪明的聪聪  阅读(719)  评论(9编辑  收藏  举报