Description
While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ’s farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1…N, M (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.
As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself 😃 .
To help FJ find out whether this is possible or not, he will supply you with complete maps to F (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.
Input
Line 1: A single integer, F. F farm descriptions follow.
Line 1 of each farm: Three space-separated integers respectively: N, M, and W
Lines 2…M+1 of each farm: Three space-separated numbers (S, E, T) that describe, respectively: a bidirectional path between S and E that requires T seconds to traverse. Two fields might be connected by more than one path.
Lines M+2…M+W+1 of each farm: Three space-separated numbers (S, E, T) that describe, respectively: A one way path from S to E that also moves the traveler back T seconds.
Output
Lines 1…F: For each farm, output “YES” if FJ can achieve his goal, otherwise output “NO” (do not include the quotes).
Sample Input
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
Sample Output
NO
YES
Hint
For farm 1, FJ cannot travel back in time.
For farm 2, FJ could travel back in time by the cycle 1->2->3->1, arriving back at his starting location 1 second before he leaves. He could start from anywhere on the cycle to accomplish this.
题意,田间小路每条path会花费t时间,而存在单一通向的虫洞让你传送回某个路口并且时间倒流t1,问存不存在一种走法可以和过去的自己相遇 |
将题目问题模型化:
问能不能回到某个点,使得到这个点的时间比出发的时间还要早。如果把时间当做边的权值,那么问题就转变问存不存在一个出发点i使得回来的时候最短路径和小于0,d[i][i]<0不就说明比一开始的初始值(0)还要小,即是回到过去了吗?
最短路径问题
那么这个题目就可以转化为一个最短路径问题,能否存在一种最短路径使得d[i][i]<0?
一开始无脑用floyd,基本就是擦这超时的边过的。说明这个题解法并不是这样。还得优化。O(n3次方)不行,而dijkstra算法不能解决负权环问题,那么这个题改用什么算法呢?
SPFA算法
这个算法也是刚学,特地拿这个题练手。
大体思路:
我觉得这个算法和dijkstra的核心思路差不多,都是不断拓展外边,使得图联通的一种方法。只是这里并不是每一步取完最优路线就舍弃掉别的点,这里是像辐射状的将所有连接点都试探性地走一遍。然后遍历过的点因为边更新了(松弛了),又可以产生新的通路。不断循环此过程,如果存在负环,会出现d[i][i]小于0的情况,这时候它会一直循环下去,那么只用看任意一个点是否遍历过超过n次就知道有没有负权环了。
这里用队列实现上述流程,详见代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <limits.h>
#define maxn 505
using namespace std;
vector<pair<int,int> > D[maxn]; //建立pair对,用vector存,相当于二维数组 表示 i为结点向外延伸到y(first)的边(second)
int d[maxn] , vis[maxn], num[maxn]; //d[]表示到i原点距离,vis作是否在队列中的标志,num表示在队列中出现的次数
int n,m;
void init() //初始化
{
for(int i=1;i<=n;i++)
D[i].clear();
for(int i=1;i<=n;i++)
vis[i] = 0;
for(int i=1;i<=n;i++)
d[i] = 1e9;
for(int i=1;i<=n;i++)
num[i] = 0;
}
bool SPFA() //写成bool型,有负环返回真(有负环的话会一直进行循环)
{
queue<int > q;
q.push(1); //先将第一个结点push进队列,因为图是联通的,哪个开始无所谓
d[1] = 0;
num[1] = 1;
vis[1] = 1;
while(!q.empty())
{
int now = q.front(); //取队头
q.pop() , vis[now] = 0; //pop掉,清除标记
for(int i=0;i<D[now].size();i++) //列出与当前结点相连接的点,看看是否可以使边松弛
{
int v =D[now][i].first;
// cout<<D[now][i].first<<' '<<now<<endl;
if(d[now] + D[now][i].second <d[v]) //原点到now点距离加上该边,小于原点到now连接点的距离,说明可以松弛
{
d[v] = d[now] + D[now][i].second; //更新
if(vis[v]) continue;
q.push(v); //若不在队列中则push进来
num[v]++; //奇数
vis[v] = 1; //更新
}
if(num[v] >n) return true ; //如果某个入队大于n次,说明已经形成负环进行无限循环了,已经可以return了
}
}
return false; //出循环了说明只有正环,无满足题意解
}
int main() //以下就是读入和判断了
{
int kase;
cin>>kase;
while(kase--)
{
int w;
scanf("%d%d%d",&n,&m,&w);
init();
for(int i=1;i<=m;i++)
{
int x, y ,z;
scanf("%d%d%d",&x,&y,&z);
D[x].push_back(make_pair(y,z));
D[y].push_back(make_pair(x,z));
}
for(int i=1;i<=w;i++)
{
int x, y ,z;
scanf("%d%d%d",&x,&y,&z);
D[x].push_back(make_pair(y,-z));
}
if(SPFA())
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}