LG P2850 [USACO06DEC]虫洞Wormholes

讨论版题面

虫洞(wormhole)

FJ 在农场上闲逛时,发现他的农场里有很多虫洞。虫洞是一条特殊的有向路径,当

FJ 从它的一头走到另一头后,他将被传送到过去的某个时刻。FJ 的每个农场包括

N(1<=N<=500)块按1..N 编号的草地、M(1<=M<=2500)条草地间的道路以及W(1<=W<=200)

个虫洞。

FJ 一直以来就渴望进行时间旅行,于是他开始做如下的打算:从某块草地出发,穿

过一些道路以及一些虫洞,最终回到他出发的草地。这样,他说不定能碰见过去的自

己:) 。

请你帮FJ 算一下,他是否可能找到这样的一条路。当然,FJ 会给你他的所有

F(1<=F<=5)个农场的完整的地图。没有哪条道路上需要花的时间超过10,000 秒,同时,

也没有哪个虫洞能把FJ 带回10,000 秒以前。

程序名: wormhole

输入格式:

* 第1 行: 一个正整数F,即农场总数。以下依次描述各个农场的地图

* 每个农场描述的第1 行:三个用空格隔开的整数,N、M 和W

* 每个农场描述的第2..M+1 行:每行包含三个用空格隔开的整数S、E、T,表示

编号为S 的草地和编号为E 的草地边有一条双向道路,通过它所花费的时间为T 秒。两

块草地间可能有多条道路

* 每个农场描述的第M+2..M+W+1 行:每行包含三个用空格隔开的整数S、E、T,

描述了一个起点编号为S、终点编号为E 的虫洞。穿过这个虫洞后,FJ 可以回到T 秒之



输入样例(wormhole.in):

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

输入说明:

FJ 交给你两个农场的地图。第一个农场里有三条道路以及一个虫洞,第二个农场里

有两条道路和一个虫洞。

输出格式:

* 第1..F 行: 对于每个农场,如果FJ 可以实现他回到过去的愿望,输出"YES",

否则输出"NO"(不含引号)。

输出样例(wormhole.out):

NO
YES

输出说明:

在农场1 中,FJ 无法完成他期望的时间旅行。

在农场2 中,FJ 可以沿路线1->2->3->1 旅行,这样他能在离开1 号草地前一秒回

到1 号草地。当然,从这条路线上的其他草地出发,也能达到目的。



luogu题面

题目描述

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.

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: 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.

输出格式:

Lines 1..F: For each farm, output "YES" if FJ can achieve his goal, otherwise output "NO" (do not include the quotes).

输入输出样例

输入样例#1: 复制
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
输出样例#1: 复制
NO
YES

说明

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.


存在负环时就是可以回到过去的情况,那么题目就转化成了判断图中是否存在负环的一个题目
这个就可以用DFS版的SPFA,但是如果用原来的松弛方法很慢,那么可以加速一下,把所有的距离都设置为0,这样就只有有负权边的时候才会松弛下去,很大程度上加速了运行速度,当然你也可以记录下所有负权边的起点,逐个提取跑SPFA,不过我试验了一下,把距离设置为0用时和记录所有负权边的起点的用时都是0ms,空间上记录所有负权边的起点不如距离设置为0优秀,要多占用0.05MB

但是凭我只能想到记录所有负权边起点。。。

那么下边给出所有的距离均设置为0的写法

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define For(i,l,r) for(int i=l;i<=r;++i)
using namespace std;
int read()
{
    bool t=0;
    int a=0;
    char c;
    while((c=getchar())==' '||c=='\r'||c=='\n');
    if(c=='-')
    {
        t=1;
        c=getchar();
    }
    while(isdigit(c))
    {
        a*=10;
        a+=c;
        a-='0';
        c=getchar();
    }
    return a*(t?-1:1);
}
struct line{
    int to,next,v;
}edge[5210];
int n,m,w,dis[501],last[501],que[201];
bool vis[501],t;
void setup()
{
    t=0;
    memset(last,0,sizeof last);
}
void dfs(int x)
{
    if(vis[x])
    {
        t=1;
        return;
    }
    vis[x]=1;
    int p=last[x];
    while(p&&!t)
    {
        if(dis[edge[p].to]>edge[p].v+dis[x])
        {
         dis[edge[p].to]=edge[p].v+dis[x];
         dfs(edge[p].to);
        }
        p=edge[p].next;
    }
    vis[x]=0;
}
void spfa(int s)
{
    memset(dis,0,sizeof dis);
    dfs(s);
}
void add(int from,int to,int v,int i)
{
    edge[i].to=to;
    edge[i].v=v;
    edge[i].next=last[from];
    last[from]=i;
}
int main()
{
    int f=read(),tx,ty,tz;
    For(i,1,f)
    {
        setup();
        n=read();
        m=read();
        w=read();
        m*=2;
        For(j,1,m)//这里一开始又写错了,j全写的i,还是没吸取教训。。
        {
            tx=read();
            ty=read();
            tz=read();
            add(tx,ty,tz,j++);
            add(ty,tx,tz,j);
        }
        For(j,1,w)
        {
            tx=read();
            ty=read();
            tz=read();
            que[j]=tx;
            add(tx,ty,tz*(-1),m+j);
        }
        For(j,1,w)
        {
            spfa(que[j]);
            if(t)
             break;
        }
        if(t)
         printf("YES\n");
        else
         printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值