在探索他的许多农场时,农夫约翰发现了许多令人惊奇的虫洞。虫洞是非常特殊的,因为它是一条单行道,在你进入虫洞之前把你送到目的地!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能够实现他的目标,输出“是”,否则输出“否”(不包括报价)。
记住两个重要的词组:
1,
a bidirectional path between S and E.
S和E之间的一条双向路径
2,从S到E的一条单向路径
A one way path from S to E
题目大意:有两中普通的路径,一种是双向路径,一种是单向路径(也就是虫洞),虫洞会让你从S到E地,但是所用的时间是
-T,普通的路径就很普通了。
其实这个题是一个判断负环的例题啊。
你想a<–>b<–>c<–>d~~>a,前三条是普通路径,后面d到a是虫洞,消耗的时间是负数,如果存在一个负环(比如,a-》b ,1秒,b-》c,1秒,c->d,1秒,d到a,-4秒,跑一圈后,a回到a所用时间-1秒,就叫存在负环),那么就可以满足题目要求,所以题目就变成了用Bellman—ford,去判断是否存在负环(啊哈c上有介绍,但是是基于有向边)
//基于bellman——ford处理负环
//同时处理无向边和有向边
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
int u,v,t;
}e[3000];
int dis[3000];
const int inf=0x3f3f3f3f;
int main()
{
int f,n,m,w;
scanf("%d",&f);
int i,j,k;
while(f--)
{
scanf("%d%d%d",&n,&m,&w);
//输入无向边,虽然是无向边,但是只存储一条,具体看后面的操作
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].t);
}
//存储虫洞的信息,时间去负
for(i=1,j=m+1;i<=w;i++,j++)
{
scanf("%d%d%d",&e[j].u,&e[j].v,&e[j].t);
e[j].t=-e[j].t;
}
for(i=1;i<=n;i++)
dis[i]=inf;
dis[1]=0;
k=0;
//第一层循环,松弛n次
//说一下为什么要松弛n次,
//先想为什么松弛n-1次,
//松弛n-1次之后,相当于形成了一条长度为n-1的路径,
//松弛第n次,判断这个形成的路径,会不会有点的信息被更新,如果还有就说明有了
//(以上大佬给讲的,挺有道理)
for(i=1;i<=n;i++)
{
k=0;
for(j=1;j<=m+w;j++)
{
//无向图和有向图都要更新
if(dis[e[j].v] > dis[e[j].u] + e[j].t)
{
k=1;
dis[e[j].v] = dis[e[j].u] + e[j].t;
}
//无向图更新,有向就不用了。
if(j<=m&&dis[e[j].u] > dis[e[j].v] + e[j].t)
{
k=1;
dis[e[j].u] = dis[e[j].v] + e[j].t;
}
}
}
//如果再上面的最后一次(更新n-1次)之后,没有点的信息,被修改,说明没有负环,
//此时k==0,输出NO;如果k==1,说明在第n次时更新了点的信息,说明存在负环。输出YES
printf(k==1? "YES\n" : "NO\n");
}
return 0;
}