1. 程式人生 > 實用技巧 >[USACO11JAN]Roads and Planes G「拓撲+最短路」

[USACO11JAN]Roads and Planes G「拓撲+最短路」

題目描述

這是個連結

思路分析

  • 題意很明確,求帶負邊的最短路。誒,別上去就跑 spfa 啊,這可是 USACO
  • 眾所周知,USACO 喜歡卡 \(spfa\),可是有負邊有不能用 \(Dijkstra\) ,這怎麼搞?
  • 還是先說一下 \(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;
}