题目链接:https://cn.vjudge.net/contest/314508#problem/E
翻译:
在探索他的许多农场时,农夫约翰发现了许多令人惊奇的虫洞。虫洞是非常特殊的,因为它是一条单行道,在你进入虫洞之前把你送到目的地!FJ的每一个农场都包括N(1≤N≤500)个区域,方便编号为1…N、M(1≤M≤2500)条路径和W(1≤W≤200)个虫洞。
因为FJ是一个狂热的时间旅行迷,他想做以下的事情:从某个领域开始,穿过一些路径和虫洞,在他最初离开之前的一段时间回到起始领域。也许他能认识自己:)。
为了帮助FJ了解这是否可行,他将向您提供其农场F(1≤F≤5)的完整地图。任何路径的运行时间都不会超过10000秒,任何虫洞都不能使FJ及时返回10000秒以上。
输入
第1行:一个整数,F.F农场描述如下。
每个农场的第1行:三个空间分隔的整数:n、m和w
第2行…每个农场的M+1:三个空格分隔的数字(S、E、T),分别表示:S和E之间的双向路径,需要T秒才能穿过。两个字段可以通过多个路径连接。
线M+2…每个农场的M+W+1:分别描述三个空格分隔的数字(S、E、T):从S到E的单向路径,也将旅行者向后移动T秒。
输出
第1行…F:对于每一个农场,如果FJ能够实现他的目标,输出“是”,否则输出“否”(不包括报价)。
在他最初离开之前的一段时间回到起始领域==所给的地图是否含有负权回路
Bellman-Ford的算法核心代码:
for(int k=1; k<n; k++)
for(int i=1; i<=m; i++)
if(dsi[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
解释:
dis数组的作用与Dijkstra算法中的作用一样。
看能否通过u[i]—>v[i](权值为w[i])这条边,使得一号顶点到v[i]点的距离变短。
对边进行最多n-1轮松弛操作(可以找一个量标记,提高效率)。
为什么进行n-1轮松弛操作?
1。在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1条边。
2。进行k轮,得到的就是1号顶点“最多经过k条边”到达其余各顶点的最短路径长度。
如何判断负权回路?
进行n-1次松弛操作后,最短路径不会在发生变化了,若仍然成功松弛,必然存在负权回路。
代码:
#include<cstdio>//判断是否具有负权环
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
struct node{
int u,v,w;
} e[250005];
int main(){
int N,M,W;
int dis[250005];
int T;
scanf("%d",&T);
while(T--){
int x=1;
scanf("%d%d%d",&N,&M,&W);//N个点,M条路径,W个虫洞
int t1,t2,t3;
for(int i=0; i<M; i++){//正常的双向道路
scanf("%d%d%d",&t1,&t2,&t3);
e[x].u=t1,e[x].v=t2,e[x++].w=t3;
e[x].u=t2,e[x].v=t1,e[x++].w=t3;
}
for(int i=0; i<W; i++){//单向的黑洞
scanf("%d%d%d",&t1,&t2,&t3);
e[x].u=t1,e[x].v=t2,e[x++].w=-t3;
}
for(int i=1; i<=N; i++)
dis[i]=INF;
dis[1]=0;
int check;
for(int j=1; j<N; j++){
check=0;//用来标记在本轮操作中dis是否更新
for(int i=1; i<x; i++){
if(dis[e[i].v]>dis[e[i].u]+e[i].w){
dis[e[i].v]=dis[e[i].u]+e[i].w;
check=1;
}
}
if(check==0)
break;
}
if(check==0)
printf("NO\n");
else{
int flag=0;
for(int i=0; i<x; i++)//是否仍然可以更新
{
if(dis[e[i].v]>dis[e[i].u]+e[i].w)
{
flag=1;
break;
}
}
if(flag==1)
printf("YES\n");
else
printf("NO\n");
}
}
return 0;
}