题目:道路与航线
网址: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
#include
#include
#include
#include
#include
using namespace std;
const int SIZE = 500000 + 10, INF = 1 << 30;
bool v[SIZE] = {}, vis[SIZE];
vector 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 > Q;
queue 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;
}