虫洞
题目描述
核心思路
这题主要是要读懂题目,刚开始读不懂,后来再理解这题其实就是一个裸的求负环。
虫洞它是单向边,路径是双向边,因此建图时,图中就会既有单项边,又有双向边。”通过虫洞,它可以回到过去的某个时刻“这句话的意思其实就是说明虫洞的单向边是负值
读懂题目的意思后,用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;
}