题目描述

John 在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John 的每个农场有 M条小路(无向边)连接着 N从 1到 N标号)块地,并有 W个虫洞。现在 John 想借助这些虫洞来回到过去(在出发时刻之前回到出发点),请你告诉他能办到吗。 John 将向你提供F 个农场的地图。没有小路会耗费你超过 10^4 的时间,当然也没有虫洞回帮你回到超过 10^4秒以前。

输入

第一行一个整数F表示农场个数;对于每个农场:第一行,三个整数 N,M,W;接下来 M行,每行三个数 S,E,T 表示在标号为 S的地与标号为 E的地中间有一条用时 T秒的小路;接下来 W行,每行三个数 S,E,T 表示在标号为 S的地与标号为 E的地中间有一条可以使 John 到达 T秒前的虫洞。

输出

输出共 F行,如果 John 能在第 I个农场实现他的目标,就在第 I行输出 YES,否则输出 NO。

数据范围限制

对于全部数据:
1<=F<=5
1<=N<=500
1<=M<=2500
1<=W<=200
1<=S,E<=N
|T|<=10^4

迷人呐。

这道题一看其实就有思路了,因为他给我们一些富有魅力的词汇:“有向边” “有M条小路(无向边)”。于是我们不难想到最短路求解。
我一开始想要的是dijkstra,原因是被样例给迷惑了,其实他并没有说虫洞是从哪到哪,而样例都是从终点到第一个点。

我们不难看出路是双向的,而虫洞只有单向且权值为负。题目中:“现在 John 想借助这些虫洞来回到过去(在出发时刻之前回到出发点)”的意义就是说如果一号点到一号点的最短路为负,那么输出YES。然后刚看题意时我还很懵(´・ω・`)(不知道农场是啥),后来才发现一个农场相当于一组测试数据

比赛交了两次,然后竟没AC——第一次visit数组(v数组,记录是否在队列中)太小,我把它开大了。然后75分,俺含着泪看了代码,链式前向星开小了(双向边×2)(┳_┳)...。

好了,裸的负回路SPFA。
代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int n,m,w;
struct edge{
    int x;//起点
    int y;//终点
    int val;//权值
    int last;//以x为起点的上一条路在e(链式前向星)中的位置
}e[6000];
int head[2000];//head[i]表示以i为起点的最后一条路在e中的位置
int cnt;//道路数量

int dis[2000];//记录最短路
int v[2000];//是否在队列中
queue<int> q;//队列

void add(int a, int b, int v)//记录道路和虫洞(链式前向星)
{
    ++cnt;
    e[cnt].x = a;
    e[cnt].y = b;
    e[cnt].val = v;
    e[cnt].last = head[a];
    head[a] = cnt;
}

void spfa()
{
    memset(dis,0x3f3f3f3f,sizeof(dis));
    memset(v,0,sizeof(v));

    dis[1] = 0;
    q.push(1);//1进队列
    v[1] = 1;

    while(!q.empty())
    {
        if(dis[1] < 0)
            return;
        int r = q.front();
        for(int i = head[r]; i != 0; i = e[i].last)
        {
            int k = e[i].y;
            if(dis[r] + e[i].val < dis[k])
            {
                dis[k] = dis[r] + e[i].val;
                if(v[k] == 0)//当k不在队列中(差点忘记呀)
                {
                    q.push(k);
                    v[k] = 1;
                }
            }
        }
        q.pop();
        v[r] = 0;
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        cnt = 0;
        memset(e,0,sizeof(e));
        memset(head,0,sizeof(head));
        scanf("%d%d%d", &n, &m, &w);
        for(int i = 1; i <= m; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, c);
            add(b, a, c);
        }
        for(int i = 1; i <= w; i++)
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            add(a, b, -c);//只记录一次
        }
        spfa();
//      for(int i = 1; i <= n; i++)//测试不用管
//          printf("%d\n",dis[i]);//测试不用管 + 1
        if(dis[1] < 0)
            printf("YES");
        else
            printf("NO");
        printf("\n");
    }
        return 0;
}
01-08 22:49