題目鏈接: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成果: