中國大學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成果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM