【问题描述】
约翰在他的农场中闲逛时发现了许多虫洞。虫洞可以看着一条十分奇特的有向边,并可以使你返回到过去的时刻(相对你进入虫洞之前)。约翰的每个农场有m条无向小路连接着n块地,并有w个虫洞。现在约翰向借助这些虫洞回到过去(出发时刻之前并且位置处于起点处),请你告诉他能办到吗?
约翰向你提供了T个农场的地图。没有小路会耗费你超过10000秒的时间,当然没有虫洞会帮你回到超过10000秒之前。
【输入格式】
第1行一个整数T,表示农场数。
接下来,对于每个农场有若干行,第1行输入三个整数n,m,w,接下来m行每行输入三个整数a,b,time,表示标号为a的地与标号为b的地用时time秒的小路。接下来w行,每行输入三个整数a,b,time,表示标号为a的地与标号为b的地中间有一条可以使约翰到达T秒前的虫洞。
【输出格式】
如果约翰在这个农场实现他的目标,输出’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
【样例解释】
对于农场2,FJ能通过1->2->3->1回到其出发时刻的前1秒。
【数据范围】
1<=F<=5 1<=n<=500 1<=m<=2500 1<=w<=200
【思路梳理】
要想能回到之前的某一个位置且使得时间比之前更早,说明必须要形成负权回路,使得能够不断地进入虫洞(权值为负的边)来使得权值之和比进入回路之前更小。所以直接判断是否存在这样的负权回路即可。
【Cpp代码】
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#define maxn 505
#define inf 5000005
using namespace std;
int t,n,m,num,dist[maxn],qn[maxn];
vector<int>g[maxn],w[maxn];
bool inq[maxn];
void initial()
{
for(int i=1;i<maxn;i++) g[i].clear(),w[i].clear();
for(int i=1;i<=n;i++) dist[i]=inf;
memset(qn,0,sizeof(qn));
memset(inq,0,sizeof(inq));
}
bool SPFA(int s)
{
queue<int>q;
q.push(s);
dist[s]=0;
inq[s]=true;
qn[s]++;
while(!q.empty())
{
int i=q.front();q.pop();inq[i]=false;
for(int j=0;j<g[i].size();j++)
{
int k=g[i][j],c=w[i][j];
if(dist[k]>dist[i]+c)
{
dist[k]=dist[i]+c;
if(!inq[k])
{
inq[k]=true;
qn[k]++;
if(qn[k]>n) return false;
q.push(k);
}
}
}
}
return true;
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>t;
while(t--)
{
scanf("%d%d%d",&n,&m,&num);
initial();
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a].push_back(b);w[a].push_back(c);
g[b].push_back(a);w[b].push_back(c);
}
for(int i=1;i<=num;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a].push_back(b);
w[a].push_back(-c);
}
if(SPFA(1)) printf("NO\n");
else printf("YES\n");
}
return 0;
}