Wormholes
Time Limit: 2000MS |
| Memory Limit: 65536K |
Total Submissions: 61855 |
| Accepted: 23054 |
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
Sample Output
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.
Source
USACO 2006 December Gold
算法分析:
题意:
N块地,M条路,W个虫洞。判断有没有可以是时间倒流的路径。
分析:
开始的思想,是把有虫洞连接两点的最短路求出,然后判断虫洞时间是否大于最短路,然后一直wrong ,题意好像是遇到虫洞就必须进虫洞。
未过代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
using namespace std;
const double eps = 1e-8;
typedef long long LL;
typedef unsigned long long ULL;
const int INT_INF = 0x3f3f3f3f;
const int INT_M_INF = 0x7f7f7f7f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
const int MOD = 1e9 + 7;
const double pi = acos(-1.0);
const int MAXN = 1e5 + 10;
const int MAXT = 10000 + 10;
const int M=2005;
int n,m,f,dis[M],vis[M];
vector<int>g[M],w[M];
int spfa(int s)
{
for(int i=0;i<=n;i++)
dis[i]=INT_INF;
memset(vis,0,sizeof(vis));
queue<int>q;
q.push(s);
dis[s]=0;
vis[s]=1;
while(!q.empty())
{
int i=q.front();
q.pop();
vis[i]=0;
for(int j=0;j<g[i].size();j++)
{
int k=g[i][j];
if(dis[k]>dis[i]+w[i][j])
{
dis[k]=dis[i]+w[i][j];
if(!vis[k])
{
vis[k]=1;
q.push(k);
}
}
}
}
return 0;
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%d%d%d",&n,&m,&f);
memset(g,0,sizeof(g));
memset(w,0,sizeof(w));
for(int i=1;i<=m;i++)//这里要从一开始
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
g[a].push_back(b);//g记录该点连接的点
g[b].push_back(a);
w[a].push_back(d);
w[b].push_back(d);
}
int flag=0;
for(int i=1;i<=f;i++)
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
if(flag==0)
{
spfa(a);
if(dis[b]<d)
flag=1;
}
}
if(flag==1)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
网上搜索是一个判断负边环,
我们考虑一个节点入队的条件是什么,只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。同样,我们有这样的定理:“两点间如果有最短路,那么每个结点最多经过一次。也就是说,这条路不超过n-1条边。”(如果一个结点经过了两次,那么我们走了一个圈。如果这个圈的权为正,显然不划算;如果是负圈,那么最短路不存在;如果是零圈,去掉不影响最优值)。也就是说,每个点最多入队n-1次(这里比较难理解,需要仔细体会,n-1只是一种最坏情况,实际中,这样会很大程度上影响程序的效率)。
有了上面的基础,思路就很显然了,加开一个数组记录每个点入队的次数(num),然后,判断当前入队的点的入队次数,如果大于n-1,则说明存在负权回路。
代码实现:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
using namespace std;
const double eps = 1e-8;
typedef long long LL;
typedef unsigned long long ULL;
const int INT_INF = 0x3f3f3f3f;
const int INT_M_INF = 0x7f7f7f7f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
const int MOD = 1e9 + 7;
const double pi = acos(-1.0);
const int MAXN = 1e5 + 10;
const int MAXT = 10000 + 10;
const int M=2005;
int n,m,f,dis[M],vis[M],num[M],flag=0;
vector<int>g[M],w[M];
int spfa(int s)
{
for(int i=0;i<=n;i++)
dis[i]=INT_INF;
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
queue<int>q;
q.push(s);
dis[s]=0;
vis[s]=1;
num[1]++;
while(!q.empty())
{
int i=q.front();
q.pop();
vis[i]=0;
for(int j=0;j<g[i].size();j++)
{
int k=g[i][j];
if(dis[k]>dis[i]+w[i][j])
{
dis[k]=dis[i]+w[i][j];
if(!vis[k])
{
num[k]++;
vis[k]=1;
q.push(k);
if(num[k]>n) {flag=1;break; }
}
}
}
if(flag==1) break;
}
return 0;
}
int main()
{
int T;
cin>>T;
while(T--)
{
flag=0;
scanf("%d%d%d",&n,&m,&f);
memset(g,0,sizeof(g));
memset(w,0,sizeof(w));
for(int i=1;i<=m;i++)//这里要从一开始
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
g[a].push_back(b);//g记录该点连接的点
g[b].push_back(a);
w[a].push_back(d);
w[b].push_back(d);
}
for(int i=1;i<=f;i++)
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
g[a].push_back(b);//g记录该点连接的点
w[a].push_back(-d);
}
spfa(1);
if(flag==1)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}