標籤:有用 vector queue 表示 cout clear 不能 resize 目標
題目描述
如題,給出一個有向圖,請輸出從某一點出發到所有點的最短路徑長度。
輸入輸出格式
輸入格式:
第一行包含三個整數N、M、S,分別表示點的個數、有向邊的個數、出發點的編號。
接下來M行每行包含三個整數Fi、Gi、Wi,分別表示第i條有向邊的出發點、目標點和長度。
輸出格式:
一行,包含N個用空格分隔的整數,其中第i個整數表示從點S出發到點i的最短路徑長度(若S=i則最短路徑長度為0,若從點S無法到達點i,則最短路徑長度為2147483647)
代碼:
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 struct edge{ 6 int k;//終點 7 int w;//邊權 8 //node(int kk, int ww):k(kk), w(ww){} 9 };10 bool operator <(const edge&n1, const edge&n2){11 return n1.w>n2.w;12 }13 priority_queue<edge>pq;14 vector<vector<edge> >v;15 const int inf = 2147483647;16 bool used[10010];17 int d[10010];18 19 int main(){20 int n, m, s;21 cin>>n>>m>>s;22 v.clear();23 v.resize(m+1);24 int i, j;25 edge p;//(0, 0);26 for(i = 1; i <= m; i++){27 int a, b, c;28 cin>>a>>b>>c;29 p.k = b;30 p.w = c;31 v[a].push_back(p);32 }33 for(i = 1; i <= n; i++) d[i] = inf;34 d[s] = 0;35 p.k = s;36 p.w = 0;37 pq.push(p);38 while(!pq.empty()){39 p = pq.top();40 pq.pop();41 if(used[p.k])//已經求出了最短路 42 continue;43 used[p.k] = true;44 d[p.k] = p.w;45 for(i = 0; i < v[p.k].size(); i++){46 edge q;47 q.k = v[p.k][i].k;48 if(!used[q.k]&&d[q.k]>p.w+v[p.k][i].w){49 q.w = p.w + v[p.k][i].w;50 pq.push(q);51 }52 } 53 } 54 for(i=1; i<=n; i++)55 cout<<d[i]<<" ";56 return 0;57 }
備忘:
加堆最佳化的dijkstra模板。開始是照著gw的ppt寫的,然後瞪了一個小時也沒看懂for迴圈裡面那幾行,已經覺得人生絕望了。zmj幫我把程式改成了現在這樣便於理解的狀態,現在我要繼續解析一下,邊寫邊想。
用vector<vector<edge>>存圖,結構體表示一條邊,元素包括邊權和終點編號。d[i]表示從源點到編號為i的點的最短距離。
首先把源點push進堆。然後進入while迴圈。
加堆是如何?最佳化的呢?就是因為堆頂元素自動就是最近的元素。
取堆頂元素p(按照定義,這時p邊所連的點p.k是堆裡w最小,即距離“已選點集”最近的點),將p彈出(必須現在彈出,不能鬆弛完了再彈)。如果發現p.k是已經用過的點了,則跳過。如果是一個新的點,則將其標記。(pop出的點集就是“已選點集,他們全都used過了)
哎呀,寫出來果然清楚多了。因為現在p.k是距離已選點集最近的點,所以它的d值其實已經確定了,於是更新(現在的d就是最終結果)。事實上現在p.k已經是我們選中的點了(即加入點集),接下來做的就是鬆弛跟它相連的點。
依次遍曆與p.k相連的點,即q.k,如果q.k沒有用過,並且d值比新路徑大,則更新q.w(為什麼不更新d值?因為這時d值還沒有確定,當然更新也是可以的,但沒這個必要。因為當它彈出去時的w值就是最終的d值,所以在這裡只更新q.w就可以了),更新完了就把它push進堆,這樣下一輪好直接取堆頂元素。
靠,我現在終於想明白了!為什麼gw要那麼寫。因為d值在這裡實際上一直是無窮大啊。所以這句根本是廢話。彈出去的時候再更新就直接是最終的結果了啊。
再補充一句,為什麼可以不用比較就直接給q.w賦值。因為賦值完了都是要把新q push進堆裡的,我們總是取堆裡最小的,所以那些不夠小的push進去根本就無所謂,到時候就又都原封不動地彈出來了。
這個道理我tm想了一下午。寫部落格真是有奇效。
好了,我要附上gw的原代碼寫法了。orz
1 #include<iostream> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 struct edge{ 6 int k;//終點 7 int w;//邊權 8 //node(int kk, int ww):k(kk), w(ww){} 9 };10 bool operator <(const edge&n1, const edge&n2){11 return n1.w>n2.w;12 }13 priority_queue<edge>pq;14 vector<vector<edge> >v;15 const int inf = 2147483647;16 bool used[10010];17 int d[10010];18 19 int main(){20 int n, m, s;21 cin>>n>>m>>s;22 v.clear();23 v.resize(m+1);24 int i, j;25 edge p;//(0, 0);26 for(i = 1; i <= m; i++){27 int a, b, c;28 cin>>a>>b>>c;29 p.k = b;30 p.w = c;31 v[a].push_back(p);32 }33 for(i = 1; i <= n; i++) d[i] = inf;34 d[s] = 0;35 p.k = s;36 p.w = 0;37 pq.push(p);38 while(!pq.empty()){39 p = pq.top();40 pq.pop();41 if(used[p.k])//已經求出了最短路 42 continue;43 used[p.k] = true;44 d[p.k] = p.w;45 for(i = 0; i < v[p.k].size(); i++){46 edge q;47 q.k = v[p.k][i].k;48 if(used[q.k])continue;49 q.w = p.w + v[p.k][i].w;50 pq.push(q);51 } 52 } 53 for(i=1; i<=n; i++)54 cout<<d[i]<<" ";55 return 0;56 }
相信這一下午沒有白費,對dijkstra的理解應該還是很深刻的(這樣安慰自己)。
3371 【模板】單源最短路徑