title

「BZOJ2200」[Usaco2011 Jan] 道路和航线 - 最短路+拓扑排序

->点我进原题

[Usaco2011 Jan]道路和航线


Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 1116 Solved: 410


### Description Farmer John正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到T个城镇 ($1 <= T <= 25,000$),编号为$1$~$T$。这些城镇之间通过$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$) 把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。 ### Input 第$1$行:四个空格隔开的整数: $T$, $R$, $P$, and $S$ 第$2到R+1$行:三个空格隔开的整数(表示一条道路):$A_i$, $B_i$ 和 $C_i$ 第$R+2到R+P+1$行:三个空格隔开的整数(表示一条航线):$A_i$, $B_i$ 和 $C_i$

Output

\(1到T\)行:从\(S\)到达城镇\(i\)的最小花费,如果不存在输出"NO PATH"。

Sample Input

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

样例输入解释:

一共六个城镇。在\(1-2,3-4,5-6\)之间有道路,花费分别是\(5,5,10\)。同时有三条航线:\(3\)->\(5\)
\(4\)->\(6\)\(1\)->\(3\),花费分别是-\(100\),-\(100\),-\(10\)\(FJ\)的中心城镇在城镇\(4\)

Sample Output

NO PATH
NO PATH
5
0
-95
-100

样例输出解释:

\(FJ\)的奶牛从\(4\)号城镇开始,可以通过道路到达\(3\)号城镇。然后他们会通过航线达到\(5\)\(6\)号城镇。
但是不可能到达\(1\)\(2\)号城镇。

分析

\(1\):Dijkstra+Topsort

本题是一道明显的单源最短路问题,但图中带有负权边,不能使用Dijkstra算法。若直接用SPFA算法求解,因为测试数据经过了特殊构造,所以程序无法在规定时限内输出答案。题目中有一个特殊条件——双向边都是非负的,只有单向边可能是负的,并且单向边不构成环。我们应该利用这个性质来解答本题。
如果只把双向边(道路)添加到图里,那么会形成若干个连通块。若把每个连通块整体看作一个“点”,再把单向边(航线)添加到图里,会得到一张有向无环图。在有向无环图中,无论边权正负,都可以按照拓扑排序进行扫描,在线性时间内求出单源最短路。这启发我们用拓扑排序的框架处理整个图,但在双向边构成的每个连通块内部使用堆优化的Dijkstra算法快速计算该块内的最短路信息
这样就可以在块内使用Dijkstra,块间利用拓扑排序更新答案。时间复杂度\(O(MlogN)\)

\(2\):SPFA+SLF

顺便加上了一些卡常的奇技淫巧,最慢的点为732ms,如果不了解SLF优化的可以看我的另外一篇博客->戳我

代码

\(1\)

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<queue>
#include<bits/stdc++.h>
#define rg register
using namespace std;
inline int read(){
	rg int f=0,x=0;
	rg char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) 	x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}

const int N =25010;
const int M =100010;
const int inf =0x7f7f7f7f;
#define ft first
#define sd second
int n,r,p,s,head[N],tot;
int cnt,belong[N];
int indeg[N],dis[N];
bool vis[N];
typedef pair<int ,int > pa;
vector <pa >road[N],plane[N];
vector <int >block[N];
inline void init(){
	for(rg int i=0;i<N;++i)	dis[i]=inf;
	dis[s]=0;
}
inline void dfs(rg int u){
	belong[u]=cnt;
	block[cnt].push_back(u);
	for(int i=0;i<road[u].size();++i){
		int v=road[u][i].ft;
		if(!belong[v])	dfs(v);
	}	
}
inline void topsort_dij(){
	init();
	queue<int > q;
	for(rg int i=1;i<=cnt;++i)	if(!indeg[i])	q.push(i);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		priority_queue<pa,vector<pa>,greater<pa> > pq;
		for(int i=0;i<block[u].size();++i)
			if(dis[block[u][i]]!=inf)
				pq.push(make_pair(dis[block[u][i]],block[u][i]));
		while(!pq.empty()){
			int u=pq.top().sd;
			pq.pop();
			if(vis[u]) continue;
			vis[u]=true;
			for(int i=0;i<road[u].size();++i)
				if(dis[u]+road[u][i].sd<dis[road[u][i].ft])
					pq.push(make_pair(dis[road[u][i].ft]=dis[u]+road[u][i].sd,road[u][i].ft));
			for(int i=0;i<plane[u].size();++i)
				dis[plane[u][i].ft]=min(dis[plane[u][i].ft],dis[u]+plane[u][i].sd);
		}
		for(int i=0;i<block[u].size();++i)
			for(int j=0;j<plane[block[u][i]].size();++j)
				if(--indeg[belong[plane[block[u][i]][j].ft]]==0)
					q.push(belong[plane[block[u][i]][j].ft]);
	}
}
signed main(){
	n=read(),r=read(),p=read(),s=read();
	for(rg int i=1,u,v,w;i<=r;++i){
		u=read(),v=read(),w=read();
		road[u].push_back(make_pair(v,w));
		road[v].push_back(make_pair(u,w));
	}
	for(rg int i=1;i<=n;++i)
		if(!belong[i]){
			++cnt;
			dfs(i);
		}
	for(rg int i=1,u,v,w;i<=p;++i){
		u=read(),v=read(),w=read();
		plane[u].push_back(make_pair(v,w));
		++indeg[belong[v]];
	}
	topsort_dij(); 
	for(rg int i=1;i<=n;++i)
		if(dis[i]==inf)	printf("NO PATH\n");
		else	printf("%d\n",dis[i]);
	return 0;
}

\(2\)

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<queue>
#define rg register
using namespace std;
inline int read(){
	rg int f=0,x=0;
	rg char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) 	x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}

const int N =25010;
const int M =150010;
const int inf =0x7f7f7f7f;
int n,r,p,s,head[N],tot,dis[N];
bool vis[N];
struct edge{
	int to,nxt,w;
}e[M];
inline void add(rg int u,rg int v,rg int w){
	e[++tot].to=v;
	e[tot].w=w;
	e[tot].nxt=head[u];
	head[u]=tot;
}
inline void spfa(rg int s){
	for(rg int i=1;i<=n;++i)	dis[i]=inf;
	dis[s]=0;
	deque<int > q;
	q.push_back(s);
	while(!q.empty()){
		int u=q.front();
		q.pop_front();
		vis[u]=false;
		for(rg int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]){
					vis[v]=true;
					if(q.empty()||dis[v]>dis[q.front()])	q.push_back(v);
					else	q.push_front(v);
				}
			}
		}
	}
	
}
signed main(){
	n=read(),r=read(),p=read(),s=read();
	for(rg int i=1;i<=r;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w),add(v,u,w);
	}
	for(rg int i=1;i<=p;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w);
	}
	spfa(s);
	for(rg int i=1;i<=n;++i)
		if(dis[i]==inf)	printf("NO PATH\n");
		else	printf("%d\n",dis[i]);
	return 0;
}
posted @ 2018-09-13 17:18  Horrigue_JyowYang  阅读(181)  评论(0编辑  收藏  举报