题目描述
有两种边:1.无向无负边权 2.有向有负边权
由于负边权的存在,无法直接使用dijkstra
而这道题目会卡spfa,因此我们需要去考虑其他的方法
我们可以先将第1种边连上,然后进行tarjan缩点(貌似并查集会更简单)
然后,对于生成的DAG图,可以用拓扑来求每个缩点后的点之间的最短路,每个集团内部的最短路,可以用dijkstra来计算
代码
#include<bits/stdc++.h>
using namespace std;
const pair<int,int> PII;
const int maxn=50000+5;
const int inf=0x3f3f3f3f;
int t,r,p,s,tot,times,in[maxn],col[maxn],instack[maxn],head[maxn],fir[maxn],dfn[maxn],low[maxn],vis[maxn],col_num;
struct edge
{
int to,nxt,v;
}e[maxn<<1],m[maxn<<1];
void add(int x,int y,int z)
{
e[++tot].nxt=head[x];
head[x]=tot;
e[tot].to=y;
e[tot].v=z;
}
int top,cnt,dis[maxn];
vector <int> G[maxn];
void tarjan(int x)
{
low[x]=dfn[x]=++times;
instack[++top]=x;
for(int i=head[x];i;i=e[i].nxt)
{
int to=e[i].to;
if(!dfn[to])
{
tarjan(to);
low[x]=min(low[x],low[to]);
}
else if(!col[to])
{
low[x]=min(low[x],dfn[to]);
}
}
if(low[x]==dfn[x])
{
col[x]=++col_num;
G[col_num].push_back(x);
while(instack[top]!=x)
{
col[instack[top]]=col_num;
G[col_num].push_back(instack[top]);
--top;
}
--top;
}
}
void add_edge(int a,int b,int c)
{
m[++cnt].nxt=fir[a];
m[cnt].to=b;
m[cnt].v=c;
fir[a]=cnt;
}
priority_queue <pair <int,int> > q;
queue <int> que;
void dijkstra()
{
while(!q.empty())
{
int u=q.top().second;
q.pop(); if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt)
{
int to=e[i].to;int v=e[i].v;
if(dis[u]<inf && dis[to]>dis[u]+v)
{
dis[to]=dis[u]+v;
q.push(make_pair(-dis[to],to));
}
}
for(int i=fir[u];i;i=m[i].nxt)
{
int to=m[i].to;
if(dis[u]<inf && dis[to]>dis[u]+m[i].v)
dis[to]=dis[u]+m[i].v;
in[col[to]]--;
if(!in[col[to]]) que.push(col[to]);
}
}
}
void toposort(int x)
{
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=col_num;i++)
if(!in[i]) que.push(i);
dis[x]=0;
while(!que.empty())
{
int u=que.front();
que.pop();
for(int k=0;k<G[u].size();k++)
{
int to=G[u][k];
q.push(make_pair(-dis[to],to));
}
dijkstra();
}
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%d%d%d%d",&t,&r,&p,&s);
int a,b,c;
for(int i=1;i<=r;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c); add(b,a,c);
}
for(int i=1;i<=t;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=p;i++)
{
scanf("%d%d%d",&a,&b,&c);
add_edge(a,b,c);
if(col[a]!=col[b]) in[col[b]]++;
}
//DAG有负权,求dis
toposort(s);
for(int i=1;i<=t;i++)
if(dis[i]==0x3f3f3f3f) printf("NO PATH\n");
else printf("%d\n",dis[i]);
return 0;
}

本文介绍了一种解决含有负权边的有向图最短路径问题的方法,通过Tarjan算法进行缩点处理,将图转化为DAG,再利用拓扑排序和Dijkstra算法计算最短路径。
335

被折叠的 条评论
为什么被折叠?



