「BZOJ2200」[Usaco2011 Jan] 道路和航线 - 最短路+拓扑排序
[Usaco2011 Jan]道路和航线
Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 1116 Solved: 410
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;
}