「BZOJ2200」[Usaco2011 Jan] 道路和航线 - 最短路+拓扑排序
->點我進原題
[Usaco2011 Jan]道路和航線
Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 1116 Solved: 410
Description
Farmer John正在一個新的銷售區域對他的牛奶銷售方案進行調查。他想把牛奶送到T個城鎮 (\(1 <= T <= 25,000\)),編號為\(1\)~\(T\)。這些城鎮之間通過\(R\)條道路 (\(1 <= R <= 50,000\),編號為\(1\)到\(R\)) 和\(P\)條航線 (\(1 <= P <= 50,000\),編號為\(1\)到\(P\)) 連接。每條道路i或者航線i連接城鎮\(A_i\) (\(1 <= A_i <= T\))到\(B_i\) (\(1 <= B_i <= T\)),花費為\(C_i\)。對于道路,\(0 <= C_i <= 10,000\);然而航線的花費很神奇,花費\(C_i\)可能是負數(\(-10,000 <= C_i <= 10,000\))。道路是雙向的,可以從\(A_i\)到\(B_i\),也可以從\(B_i\)到\(A_i\),花費都是\(C_i\)。然而航線與之不同,只可以從\(A_i\)到\(B_i\)。事實上,由于最近恐怖主義太囂張,為了社會和諧,出臺 了一些政策保證:如果有一條航線可以從\(A_i\)到\(B_i\),那么保證不可能通過一些道路和航線從\(B_i\)回到\(A_i\)。由于FJ的奶牛世界公認十分給力,他需要運送奶牛到每一個城鎮。他想找到從發送中心城鎮\(S\)(\(1 <= S <= T\)) 把奶牛送到每個城鎮的最便宜的方案,或者知道這是不可能的。
Input
第\(1\)行:四個空格隔開的整數: \(T\), \(R\), \(P\), and \(S\)
第\(2到R+1\)行:三個空格隔開的整數(表示一條道路):\(A_i\), \(B_i\) 和 \(C_i\)
第\(R+2到R+P+1\)行:三個空格隔開的整數(表示一條航線):\(A_i\), \(B_i\) 和 \(C_i\)
Output
第\(1到T\)行:從\(S\)到達城鎮\(i\)的最小花費,如果不存在輸出"NO PATH"。
Sample Input
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
樣例輸入解釋:
一共六個城鎮。在\(1-2,3-4,5-6\)之間有道路,花費分別是\(5,5,10\)。同時有三條航線:\(3\)->\(5\),
\(4\)->\(6\)和\(1\)->\(3\),花費分別是-\(100\),-\(100\),-\(10\)。\(FJ\)的中心城鎮在城鎮\(4\)。
Sample Output
NO PATH
NO PATH
5
0
-95
-100
樣例輸出解釋:
\(FJ\)的奶牛從\(4\)號城鎮開始,可以通過道路到達\(3\)號城鎮。然后他們會通過航線達到\(5\)和\(6\)號城鎮。
但是不可能到達\(1\)和\(2\)號城鎮。
分析
法\(1\):Dijkstra+Topsort
本題是一道明顯的單源最短路問題,但圖中帶有負權邊,不能使用Dijkstra算法。若直接用SPFA算法求解,因為測試數據經過了特殊構造,所以程序無法在規定時限內輸出答案。題目中有一個特殊條件——雙向邊都是非負的,只有單向邊可能是負的,并且單向邊不構成環。我們應該利用這個性質來解答本題。
如果只把雙向邊(道路)添加到圖里,那么會形成若干個連通塊。若把每個連通塊整體看作一個“點”,再把單向邊(航線)添加到圖里,會得到一張有向無環圖。在有向無環圖中,無論邊權正負,都可以按照拓撲排序進行掃描,在線性時間內求出單源最短路。這啟發我們用拓撲排序的框架處理整個圖,但在雙向邊構成的每個連通塊內部使用堆優化的Dijkstra算法快速計算該塊內的最短路信息
這樣就可以在塊內使用Dijkstra,塊間利用拓撲排序更新答案。時間復雜度\(O(MlogN)\)。
法\(2\):SPFA+SLF
順便加上了一些卡常的奇技淫巧,最慢的點為732ms,如果不了解SLF優化的可以看我的另外一篇博客->戳我
代碼
法\(1\):
#include<cstdio> #include<cctype> #include<algorithm> #include<iostream> #include<queue> #include<bits/stdc++.h> #define rg register using namespace std; inline int read(){rg int f=0,x=0;rg char ch=getchar();while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();return f?-x:x; }const int N =25010; const int M =100010; const int inf =0x7f7f7f7f; #define ft first #define sd second int n,r,p,s,head[N],tot; int cnt,belong[N]; int indeg[N],dis[N]; bool vis[N]; typedef pair<int ,int > pa; vector <pa >road[N],plane[N]; vector <int >block[N]; inline void init(){for(rg int i=0;i<N;++i) dis[i]=inf;dis[s]=0; } inline void dfs(rg int u){belong[u]=cnt;block[cnt].push_back(u);for(int i=0;i<road[u].size();++i){int v=road[u][i].ft;if(!belong[v]) dfs(v);} } inline void topsort_dij(){init();queue<int > q;for(rg int i=1;i<=cnt;++i) if(!indeg[i]) q.push(i);while(!q.empty()){int u=q.front();q.pop();priority_queue<pa,vector<pa>,greater<pa> > pq;for(int i=0;i<block[u].size();++i)if(dis[block[u][i]]!=inf)pq.push(make_pair(dis[block[u][i]],block[u][i]));while(!pq.empty()){int u=pq.top().sd;pq.pop();if(vis[u]) continue;vis[u]=true;for(int i=0;i<road[u].size();++i)if(dis[u]+road[u][i].sd<dis[road[u][i].ft])pq.push(make_pair(dis[road[u][i].ft]=dis[u]+road[u][i].sd,road[u][i].ft));for(int i=0;i<plane[u].size();++i)dis[plane[u][i].ft]=min(dis[plane[u][i].ft],dis[u]+plane[u][i].sd);}for(int i=0;i<block[u].size();++i)for(int j=0;j<plane[block[u][i]].size();++j)if(--indeg[belong[plane[block[u][i]][j].ft]]==0)q.push(belong[plane[block[u][i]][j].ft]);} } signed main(){n=read(),r=read(),p=read(),s=read();for(rg int i=1,u,v,w;i<=r;++i){u=read(),v=read(),w=read();road[u].push_back(make_pair(v,w));road[v].push_back(make_pair(u,w));}for(rg int i=1;i<=n;++i)if(!belong[i]){++cnt;dfs(i);}for(rg int i=1,u,v,w;i<=p;++i){u=read(),v=read(),w=read();plane[u].push_back(make_pair(v,w));++indeg[belong[v]];}topsort_dij(); for(rg int i=1;i<=n;++i)if(dis[i]==inf) printf("NO PATH\n");else printf("%d\n",dis[i]);return 0; }法\(2\):
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<iostream> #include<queue> #define rg register using namespace std; inline int read(){rg int f=0,x=0;rg char ch=getchar();while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();return f?-x:x; }const int N =25010; const int M =150010; const int inf =0x7f7f7f7f; int n,r,p,s,head[N],tot,dis[N]; bool vis[N]; struct edge{int to,nxt,w; }e[M]; inline void add(rg int u,rg int v,rg int w){e[++tot].to=v;e[tot].w=w;e[tot].nxt=head[u];head[u]=tot; } inline void spfa(rg int s){for(rg int i=1;i<=n;++i) dis[i]=inf;dis[s]=0;deque<int > q;q.push_back(s);while(!q.empty()){int u=q.front();q.pop_front();vis[u]=false;for(rg int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;if(!vis[v]){vis[v]=true;if(q.empty()||dis[v]>dis[q.front()]) q.push_back(v);else q.push_front(v);}}}}} signed main(){n=read(),r=read(),p=read(),s=read();for(rg int i=1;i<=r;++i){int u=read(),v=read(),w=read();add(u,v,w),add(v,u,w);}for(rg int i=1;i<=p;++i){int u=read(),v=read(),w=read();add(u,v,w);}spfa(s);for(rg int i=1;i<=n;++i)if(dis[i]==inf) printf("NO PATH\n");else printf("%d\n",dis[i]);return 0; }轉載于:https://www.cnblogs.com/horrigue/p/9641636.html
總結
以上是生活随笔為你收集整理的「BZOJ2200」[Usaco2011 Jan] 道路和航线 - 最短路+拓扑排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现代制造工程02:第二部分——机床、刀具
- 下一篇: SSM项目中整合WebService