链接:http://acm.ocrosoft.com/problem.php?cid=1713&pid=2
题目描述
John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地,并有W个虫洞。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。
输入
* Line 1: 一个整数 F, 表示农场个数。
* Line 1 of each farm: 三个整数 N, M, W。
* Lines 2..M+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条用时T秒的小路。
* Lines M+2..M+W+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条可以使John到达T秒前的虫洞。
输出
* Lines 1..F: 如果John能在这个农场实现他的目标,输出”YES”,否则输出”NO”。
样例输入
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
样例输出
NO
YES
思路
这道题是一道存在负环spfa图论的题,
虫洞就是边权为负的边。用spfa判负权回路:如果任意一条边被修改大于n-1次,就代表这个图内一定存在至少一个负权回路。如果单纯的用spfa来写会被卡,因此我们要判负环来进行spfa(听说用带dfs的spfa更快)
代码:
#include<bits/stdc++.h>
using namespace std;
long long int n,m,x,y,z,w,t;
queue<int>q;
int dis[505],cnt[505],p[505][505];//p代表权值
bool f[505];//标记是否经过该点
int spfa(int s)
{
for(int i=1;i<=n;i++)//初始化
{
dis[i]=10000007;
cnt[i]=0;
}
q.push(s);//入队
f[s]=true;
dis[s]=0;
while(q.size())
{
int x=q.front();
q.pop();//出队
f[x]=false;
for(int i=1;i<=n;i++)
{
if(dis[x]+p[x][i]<dis[i])
{
dis[i]=dis[x]+p[x][i];
if(!f[i])
{
q.push(i);
cnt[i]++;
if(cnt[i]==n)//松弛超过n次就说明有负环
{
return 1;
}
f[i]=true;
}
}
if(dis[1]<0)
{
return 1;
}
}
}
return 0;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m>>w;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
p[i][j]=10000007;//初始化边权最大值
}
}
for(int i=1;i<=m;i++)//建双向边
{
cin>>x>>y>>z;
p[x][y]=min(z,p[x][y]);
p[y][x]=p[x][y];
}
for(int i=1;i<=w;i++)
{
cin>>x>>y>>z;
p[x][y]=min(-z,p[x][y]);
}
if(spfa(1))//spfa
{
printf("YES\n");
continue;
}
printf("NO\n");
}
return 0;
}