虫洞---

虫洞


题目描述

image-20210711231829264


核心思路

这题主要是要读懂题目,刚开始读不懂,后来再理解这题其实就是一个裸的求负环。

虫洞它是单向边,路径是双向边,因此建图时,图中就会既有单项边,又有双向边。”通过虫洞,它可以回到过去的某个时刻“这句话的意思其实就是说明虫洞的单向边是负值

读懂题目的意思后,用spfa求负环就好了


代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
//边数=路径的双向边2500*2+虫洞的单向边200=5200
const int N=510,M=5210;
//n表示田地的数量  m1表示路径的数量  m2表示虫洞单向边的数量
int n,m1,m2;
int h[N],e[M],ne[M],w[M],idx;
int q[N],cnt[N],dist[N];
bool st[N];
//建图
void add(int a,int b,int c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
//spfa求负环
bool spfa()
{
    //有多组测试数据 要初始化
    memset(dist,0x3f,sizeof dist);  //求负环时  对于dist数组没有任何要求,不用初始化都可以
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    int hh=0,tt=1;
    //在新图中添加一个虚拟源点就等效于 一开始将原图中的所有节点都加入循环队列q中
    for(int i=1;i<=n;i++)
    {
        q[tt++]=i;
        //这里从初始化为0 其实含义就是:在新图中,虚拟源点到其他n个节点连一条长度为0的边
        dist[i]=0;
        st[i]=true;
    }
    while(hh!=tt)
    {
        int t=q[hh++];
        if(hh==N)
        hh=0;
        st[t]=false;
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                cnt[j]=cnt[t]+1;
                //存在负环  那么他可以回到过去
                if(cnt[j]>=n) 
                return true;
                if(!st[j])
                {
                    q[tt++]=j;
                    if(tt==N)
                    tt=0;
                    st[j]=true;
                }
            }
        }
    }
    //说明不存在负环  那么他不可用回到过去
    return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(h,-1,sizeof h);
        idx=0;
        scanf("%d%d%d",&n,&m1,&m2);
        //给路径的双向边建图
        while(m1--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
        }
        //给虫洞的单向边建图
        while(m2--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            //注意边的权值是-c
            add(a,b,-c);
        }
        //存在负环 则可以回到过去
        if(spfa())
        puts("YES");
        //不存在负环 则不可以回到过去
        else
        puts("NO");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值