[Usaco2011 Jan]道路和航線
Farmer John正在一個新的銷售區域對他的牛奶銷售方案進行調查。他想把牛奶送到T個城鎮 (1 <= T <= 25,000),編號為1T。這些城鎮之間通過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) 把奶牛送到每個城鎮的最便宜的方案,或者知道這是不可能的。
這題又是比較賤的卡了SPFA。。
改DIJ後你就會發現。。 dij不能寫有負邊得題啊。
不過題目中有個重要的條件
如果有一條航線可以從A_i到B_i,那麼保證不可能通過一些道路和航線從B_i回到A_i
這個條件很重要
如果我們把整個圖縮點之後,就會發現,這種負邊一定是連線著不同的兩個連通塊!
那麼我們就可以在每個連通塊內進行dijkstra了
然後就會發現需要進行拓撲排序才行。
然後我就不知道為啥wa了一天。。估計寫的太亂了
我的方法是這樣。
首先將整個圖縮點。
然後把從源點到達不了的邊都給幹掉。
這樣是方便我們拓撲排序用的。
然後把源點所在的聯通塊編號插入到佇列中,開始拓撲排序。
拓撲過程中每次都要進行dijkstra,將某個聯通塊內的最短路求出來
我們用一些vector記錄每個聯通塊中從別的連通塊可以連邊進來的點。
那麼每次dijkstra時,就用這些點作為初始狀態進行擴充套件
求完dijkstra後就開始刪邊,找下一個拓撲序列中的連通塊, 同時更新每個聯通塊中從別的連通塊可以連邊進來的點。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> #include <stack> #define MAXN 55555 #define INF 1000010080 using namespace std; typedef pair<int, int> P; priority_queue<P, vector<P>, greater<P> > q; vector<P> g[MAXN]; int dis[MAXN], vis[MAXN], cango[MAXN]; int src, n, R, m; int scc, ind; int dfn[MAXN], instack[MAXN], low[MAXN], fa[MAXN], in[MAXN], last[MAXN], num[MAXN], c[MAXN]; stack<int>st; vector<int>bl[MAXN], got[MAXN]; void tarjan(int u) { dfn[u] = low[u] = ++ind; st.push(u); instack[u] = 1; int sz = g[u].size(); for(int i = 0; i < sz; i++) { int v = g[u][i].first; if(!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if(instack[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { scc++; while(true) { int tmp = st.top(); st.pop(); instack[tmp] = 0; fa[tmp] = scc; bl[scc].push_back(tmp); if(tmp == u) break; } } } int que[MAXN]; int l, r; void dij(int now) { while(!q.empty()) q.pop(); for(int i = 0; i < got[now].size(); i++) { int u = got[now][i]; q.push(P(dis[u], u)); } while(!q.empty()) { P p = q.top(); q.pop(); int u = p.second, d = p.first; if(vis[u]) continue; vis[u] = 1; int sz = g[u].size(); for(int i = 0; i < sz; i++) { int v = g[u][i].first; int w = g[u][i].second; if(fa[v] == fa[u]) { if(d + w < dis[v]) { dis[v] = d + w; q.push(P(dis[v], v)); } } } } } void dfs(int u) { cango[u] = 1; int sz = g[u].size(); for(int i = 0; i < sz; i++) { int v = g[u][i].first; if(!cango[v]) dfs(v); } } void gao() { que[r++] = fa[src]; while(l < r) { int now = que[l++]; dij(now); int sz = bl[now].size(); for(int i = 0; i < sz; i++) { int u = bl[now][i]; for(int j = 0; j < g[u].size(); j++) { int v = g[u][j].first; int w = g[u][j].second; if(fa[v] != now) { --in[fa[v]]; if(dis[u] + w < dis[v]) dis[v] = dis[u] + w, got[fa[v]].push_back(v); if(in[fa[v]] == 0) que[r++] = fa[v]; } } } } } int main() { int u, v, w; scanf("%d%d%d%d", &n, &R, &m, &src); for(int i = 1; i <= n; i++) dis[i] = INF; dis[src] = 0; for(int i = 0; i < R; i++) { scanf("%d%d%d", &u, &v, &w); g[u].push_back(P(v, w)); g[v].push_back(P(u, w)); } for(int i = 0; i < m; i++) { scanf("%d%d%d", &u, &v, &w); g[u].push_back(P(v, w)); } for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i); got[fa[src]].push_back(src); dfs(src); for(int i = 1; i <= n; i++) { if(!cango[i]) continue; int sz = g[i].size(); for(int j = 0; j < sz; j++) { v = g[i][j].first; if(fa[v] != fa[i]) in[fa[v]]++; } } gao(); for(int i = 1; i <= n; i++) { if(dis[i] == INF) printf("NO PATH\n"); else printf("%d\n", dis[i]); } return 0; }