1. 程式人生 > 實用技巧 >道路與航線

道路與航線

題目:道路與航線

網址:http://www.lydsy.com/JudgeOnline/problem.php?id=4921

農夫約翰正在一個新的銷售區域對他的牛奶銷售方案進行調查。

他想把牛奶送到\(T\)個城鎮,編號為\(1~T\)

這些城鎮之間通過\(R\)條道路 (編號為\(1\)\(R\)) 和\(P\)條航線 (編號為\(1\)\(P\)) 連線。

每條道路\(i\)或者航線\(i\)連線城鎮\(A_i\)\(B_i\),花費為\(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\)

由於約翰的奶牛世界公認十分給力,他需要運送奶牛到每一個城鎮。

他想找到從傳送中心城鎮S把奶牛送到每個城鎮的最便宜的方案。

輸入格式

第一行包含四個整數\(T,R,P,S\)

接下來\(R\)行,每行包含三個整數(表示一個道路)\(A_i,B_i,C_i\)

接下來\(P\)

行,每行包含三個整數(表示一條航線)\(A_i,B_i,C_i\)

輸出格式

\(1..T\)行:第\(i\)行輸出從\(S\)到達城鎮\(i\)的最小花費,如果不存在,則輸出“NO PATH”。

資料範圍

\(1≤T≤25000,1≤R,P≤50000,1≤A_i,B_i,S≤T,\)

輸入樣例:
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
輸出樣例:
NO PATH
NO PATH
5
0
-95
-100
解析

這道題看似是一道單源最短路的板子題。
因為有負邊,所以只好使用SPFA;但時間複雜度又會炸。

題目所給出的圖是有特點的。所有的無向邊權均為正,把由無向邊連線的結點縮點,有向邊不會構成環。

縮點,然後在新的有向圖中按拓撲排序便可以求得源點所在聯通分量到其他聯通分量的最短路徑。

對於單獨的聯通分量,可以跑Dijkstra。

由此,我們先把無向邊建立來,把所有的跟自己聯通的結點做相同記號;

  • 按照拓撲排序,把這些聯通分量的標號一一敲進佇列中;
  • 取出隊首,對它進行最短路的運算,不斷更新周圍的結點;
  • 如果更新的是自己人,那麼按照正常的Dijkstra,將其扔進堆中;
  • 不管是否能更新,只要是其它的聯通塊的結點,我們都應將該結點所在聯通塊入度減一;
  • 按照拓撲序進行。
程式碼
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
const int SIZE = 500000 + 10, INF = 1 << 30;
bool v[SIZE] = {}, vis[SIZE];
vector <int> G[SIZE], W[SIZE], color[SIZE];
int T, R, P, S, dfn[SIZE], tot = 0, deg[SIZE], dis[SIZE];
void dfs(int x)
{
	if(v[x]) return;
	v[x] = true;
	if(dfn[x] == 0) dfn[x] = ++ tot;
	color[dfn[x]].push_back(x);
	int y;
	for(int i = 0; i < G[x].size(); ++ i)
	{
		y = G[x][i];
		if(!v[y]) 
		{
			dfn[y] = dfn[x];
			dfs(y);
		}
	}
	return;
}
void init()
{
	memset(dfn, false, sizeof(dfn));
	memset(v, false, sizeof(v));
	memset(deg, 0, sizeof(deg));
	memset(vis, false, sizeof(vis));
	return;
}
void topsort()
{
	priority_queue <pair <int, int> > Q;
	queue <int> q;
	while(!q.empty()) q.pop();
	while(!Q.empty()) Q.pop();
	for(int i = 1; i <= T; ++ i) dis[i] = INF;
	dis[S] = 0;
	
	for(int i = 1; i <= tot; ++ i)
	{
		if(deg[i] == 0)
		{
			q.push(i);
		}
	}
	if(deg[dfn[S]]) q.push(dfn[S]);
	while(!q.empty())
	{
		int type = q.front(); q.pop();
		for(int i = 0; i < color[type].size(); ++ i)
		{
			int x = color[type][i];
			Q.push(make_pair(-dis[x], x));
		}
		while(!Q.empty())
		{
			int x = Q.top().second; Q.pop();
			if(vis[x]) continue;
			vis[x] = true;
			for(int i = 0; i < G[x].size(); ++ i)
			{
				int y = G[x][i];
				if(dis[x] != INF && dis[y] > dis[x] + W[x][i])
				{
					dis[y] = dis[x] + W[x][i];
					if(dfn[x] == dfn[y]) Q.push(make_pair(-dis[y], y));
				}
				-- deg[dfn[y]];
				if(dfn[y] != dfn[x] && !deg[dfn[y]]) q.push(dfn[y]);//兩者均為獨立的!! 
			}
		}
	}
	return;
}
int main()
{
	scanf("%d %d %d %d", &T, &R, &P, &S);
	init();
	for(int i = 1; i <= T; ++ i) G[i].clear(), W[i].clear();
	int x, y, z;
	for(int i = 0; i < R; ++ i)
	{		
		scanf("%d %d %d", &x, &y, &z);
		G[x].push_back(y), W[x].push_back(z);
		G[y].push_back(x), W[y].push_back(z);
	}
	for(int i = 1; i <= T; ++ i) dfs(i);
	for(int i = 0; i < P; ++ i)
	{
		scanf("%d %d %d", &x, &y, &z);
		G[x].push_back(y), W[x].push_back(z);
		int c = dfn[y];
		++ deg[c];
	}
	topsort();
	for(int i = 1; i <= T; ++ i) if(dis[i] == INF) puts("NO PATH");
	else printf("%d\n", dis[i]);
	return 0;
}