[USACO11JAN]Roads and Planes G「拓撲+最短路」
阿新 • • 發佈:2020-10-12
題目描述
思路分析
- 題意很明確,求帶負邊的最短路。
誒,別上去就跑 spfa 啊,這可是 USACO。 - 眾所周知,USACO 喜歡卡 \(spfa\),可是有負邊有不能用 \(Dijkstra\) ,這怎麼搞?
- 還是先說一下 \(Dijkstra\) 為什麼不能跑負邊吧。\(Dijkstra\) 的核心是由貪心得來的,即長邊是有最短的邊鬆弛的,而在有負邊的圖中,因為負邊不論多長都會使長邊更短,所以貪心不成立
- 但是這道題的負邊很特殊,是單向的,如果先不考慮負邊的呢?那麼這時候整個圖的聯通性是無法保證的,會形成許多個由無向邊形成的聯通塊。
- 在聯通塊內沒有負邊,所以是可以跑 \(Dijkstra\)
- 接下來就很簡單了,用拓撲排序依次更新每個聯通塊。哦,對了,題目給了中心城市,所以先把中心城市所在的聯通塊加進去
\(Code\)
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #define R register #define N 200010 using namespace std; inline int read(){ int x = 0,f = 1; char ch = getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int n,r,p,s,cnt,head[N],dis[N],in[N],belong[N]; bool vis[N]; struct edge{ int to,next,dis; }e[N<<2]; int len; void addedge(int u,int v,int w){ e[++len].to = v; e[len].dis = w; e[len].next = head[u]; head[u] = len; } vector<int>sta[N]; void dfs(int u){//求出每個點所在的聯通塊 belong[u] = cnt; sta[cnt].push_back(u); for(R int i = head[u];i;i = e[i].next){ int v = e[i].to; if(belong[v])continue; dfs(v); } } struct node{ int dis,id; node(){} node(int _dis,int _id){dis = _dis,id = _id;} inline bool operator <(const node &a)const{ return dis > a.dis; } }; priority_queue<node>q; queue<int>qq; void Dij(){ while(!q.empty()){ int u = q.top().id;q.pop(); if(vis[u])continue; vis[u] = 1; for(R int i = head[u];i;i = e[i].next){ int v = e[i].to; if(dis[v]>dis[u]+e[i].dis){ dis[v] = dis[u] + e[i].dis; if(belong[u]==belong[v])q.push(node(dis[v],v)); } if(belong[u]!=belong[v]){ in[belong[v]]--; if(!in[belong[v]])qq.push(belong[v]); } } } } void Topo(){//拓撲排序以此更新每個聯通塊最短路 qq.push(belong[s]); for(R int i = 1;i <= cnt;i++)if(!in[i])qq.push(i); memset(dis,0x7f,sizeof(dis));//0x3f會wa的 dis[s] = 0; while(!qq.empty()){ int u = qq.front();qq.pop(); for(R int i = 0;i < sta[u].size();i++)q.push(node(dis[sta[u][i]],sta[u][i])); Dij(); } } int main(){ n = read(),r = read(),p = read(),s = read(); for(R int i = 1;i <= r;i++){ int a = read(),b = read(),c = read(); addedge(a,b,c); addedge(b,a,c); } for(R int i = 1;i <= n;i++){ if(!belong[i]){ cnt++; dfs(i); } } for(R int i = 1;i <= p;i++){ int a = read(),b = read(),c = read(); in[belong[b]]++;//入度++ addedge(a,b,c); } Topo(); for(R int i = 1;i <= n;i++){ if(dis[i]>=0x3f3f3f3f)puts("NO PATH"); else printf("%d\n",dis[i]); } return 0; }