【GDFZOJ 1463】郵遞員送信
阿新 • • 發佈:2020-07-23
- 每次只能帶一樣東西
- 求送完這 \(N−1\) 樣東西並且最終回到郵局最少需要多少時間。
這幾個關鍵句暗示我們:這道題需要求最短路。
我們將總路徑拆成兩類。
- 第一類是由郵局(節點\(1\))發散出來的最短路徑和。這一類顯然是單源最短路徑和。
- 第二類是由其他店到郵局(節點\(1\))的路徑和。這一類處理方法比較困難,下面羅列出我在比賽時的兩種處理方法。
部分分
每個點都跑一次Dijkstra,再求出兩點之間的最短路徑和。此方法有大量冗餘計算,效率低下,複雜度為\(O(n^2\ \log_2n)\)。
程式碼:
#include<bits/stdc++.h> using namespace std; const int N=1005,M=1e5+5; struct edge { int to,w,nxt; } e[M]; int n,tmp,head[N],vis[N],dis[N]; void add(int x,int y,int z) { e[++tmp].to=y,e[tmp].w=z,e[tmp].nxt=head[x],head[x]=tmp; } priority_queue<pair<int,int>> q; inline void dijkstra(int u) { priority_queue<pair<int,int>> q; memset(dis,0x7f,sizeof(dis)); memset(vis,0,sizeof(vis)); q.push(make_pair(0,u)),dis[u]=0; while(q.size()) { u=q.top().second,q.pop(); if(vis[u]) { continue; } vis[u]=1; for(int i=head[u],v,w; i; i=e[i].nxt) { v=e[i].to,w=e[i].w; if(dis[u]+w<dis[v]) { dis[v]=dis[u]+w; q.push(make_pair(-dis[v],v)); } } } } int main() { int m; scanf("%d %d",&n,&m); for (int i=0,u,v,w; i<m; i++) { scanf("%d %d %d",&u,&v,&w); add(u,v,w); } memset(dis,0x7f,sizeof(dis)); dijkstra(1); int ans=0; for(int i=2; i<=n; i++) { ans+=dis[i]; } for(int i=2; i<=n; i++) { dijkstra(i),ans+=dis[1]; } printf("%d",ans); return 0; }
正解
我們需要一種新的方法。
在求第二類路徑和時,我們發現:如果我們對這個圖建反圖,那麼第二類路徑和類似於第一類路徑和。我們可以用同樣的時間複雜度(\(O(n\ \log_2n)\))求解,不會超時。
程式碼:
#include<bits/stdc++.h> using namespace std; const int N=1005,M=1e5+5; int n; namespace P1 { struct edge { int to,w,nxt; } e[M]; int tmp,head[N],vis[N],dis[N]; void add(int x,int y,int z) { e[++tmp].to=y,e[tmp].w=z,e[tmp].nxt=head[x],head[x]=tmp; } priority_queue<pair<int,int>> q; inline void dijkstra(int u) { priority_queue<pair<int,int>> q; memset(dis,0x7f,sizeof(dis)); memset(vis,0,sizeof(vis)); q.push(make_pair(0,u)),dis[u]=0; while(q.size()) { u=q.top().second,q.pop(); if(vis[u]) { continue; } vis[u]=1; for(int i=head[u],v,w; i; i=e[i].nxt) { v=e[i].to,w=e[i].w; if(dis[u]+w<dis[v]) { dis[v]=dis[u]+w; q.push(make_pair(-dis[v],v)); } } } } } namespace P2 { struct edge { int to,w,nxt; } e[M]; int tmp,head[N],vis[N],dis[N]; void add(int x,int y,int z) { e[++tmp].to=y,e[tmp].w=z,e[tmp].nxt=head[x],head[x]=tmp; } priority_queue<pair<int,int>> q; inline void dijkstra(int u) { priority_queue<pair<int,int>> q; memset(dis,0x7f,sizeof(dis)); memset(vis,0,sizeof(vis)); q.push(make_pair(0,u)),dis[u]=0; while(q.size()) { u=q.top().second,q.pop(); if(vis[u]) { continue; } vis[u]=1; for(int i=head[u],v,w; i; i=e[i].nxt) { v=e[i].to,w=e[i].w; if(dis[u]+w<dis[v]) { dis[v]=dis[u]+w; q.push(make_pair(-dis[v],v)); } } } } } int main() { int m; scanf("%d %d",&n,&m); for (int i=0,u,v,w; i<m; i++) { scanf("%d %d %d",&u,&v,&w); P1::add(u,v,w),P2::add(v,u,w); } memset(P1::dis,0x7f,sizeof(P1::dis)); memset(P2::dis,0x7f,sizeof(P2::dis)); P1::dijkstra(1),P2::dijkstra(1); int ans=0; for(int i=2; i<=n; i++) { ans+=P1::dis[i]+P2::dis[i]; } printf("%d",ans); return 0; }
後記
在這道題裡,要注意計算冗餘量是否夠多(類似時間複雜度)。這道題一開始的時間複雜度\(O(n^2\ \log_2n)\)與後來的\(O(n\ \log_2n)\)區別還是很大的,例如我的提交記錄:
所以說:
-
注意時間複雜度
-
在第1條滿足後儘量減少複雜度至在時間方面大概率(甚至\(100\%\))通過。