1715: [Usaco2006 Dec]Wormholes 虫洞
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 605 Solved: 345
[ Submit][ Status][ Discuss]
Description
John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地,并有W个虫洞。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。
Input
* 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秒前的虫洞。
Output
* Lines 1..F: 如果John能在这个农场实现他的目标,输出"YES",否则输出"NO"。
Sample Input
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
YES
解题思路:首先先构图(有重边),黑洞赋予负值(注意是有向边),然后判断是否有负环。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int F;
int n,m,w;
int dis[501];
bool b[501];
int q[1000000];
int f[501][501];
int degree[501];
inline int read()
{
char y; int x=0,f=1; y=getchar();
while (y<'0' || y>'9') {if (y=='-') f=-1; y=getchar();}
while (y>='0' && y<='9') {x=x*10+int(y)-48; y=getchar();}
return x*f;
}
int main()
{
F=read();
for (int u=1;u<=F;++u)
{
n=read(); m=read(); w=read();
memset(f,0x7f,sizeof(f));
for (int i=1;i<=m;++i)
{
int s,e,t;
s=read(); e=read(); t=read();
if (s!=e)
{
f[s][e]=min(f[s][e],t); f[e][s]=min(f[e][s],t);
}
}
for (int i=1;i<=w;++i)
{
int s,e,t;
s=read(); e=read(); t=read();
if (s==e) continue;
f[s][e]=min(f[s][e],-t);
}
memset(dis,0x7f,sizeof(dis));
bool mg=false;
memset(degree,0,sizeof(degree));
memset(b,true,sizeof(b));
dis[1]=0; b[1]=false; int tail=1,head=0; q[1]=1; ++degree[1];
while (head<tail)
{
++head;
for (int i=1;i<=n;++i)
if (f[q[head]][i]<1000000)
{
if (f[q[head]][i]+dis[q[head]]<dis[i])
{
dis[i]=f[q[head]][i]+dis[q[head]];
if (b[i])
{
b[i]=false;
++degree[i]; ++tail; q[tail]=i;
if (degree[i]>n) {mg=true;break;}
}
}
}
if (mg) break;
b[q[head]]=true;
}
if (mg) printf("YES\n");else printf("NO\n");
}
}