Wormholes——SPFA判负环

题目:

描述
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: 一个整数 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秒前的虫洞。

输出

  • Lines 1…F: 如果John能在这个农场实现他的目标,输出"YES",否则输出"NO"

样例
输入
2//2组数据
3 3 1//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
输出
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.

这道题的题意就是给定一个无向图,并有k条有向的负边,让我们判断此图是否有负环。

一提到负环,Dij就只有灰溜溜地离开了,因为它不支持处理带负环的图,所以我们只有从机器猫最短路算法里掏出SPFA,并且记录每个点的出队次数,如果大于n,那就肯定存在负环,因为如过图中存在一个负环,这一圈的总距离是负数,最短路算法为了求最短,就继续走这个环,一直循环。

代码:

#include
using namespace std;
struct EDGE {
	int next;
	int to;
	int cs;
} e[99999];
int head[99999];
int num=1;
int d[100000];
int q[999999];
int vis[999999];
int mvp[999999];
int f;
int n,m,w;
int s,e1,t;
void add(int x,int y,int cs) {
	e[num].next=head[x];
	e[num].to=y;
	e[num].cs=cs;
	head[x]=num;
	num++;
}//链式前向星
int spfa(int st) {//传入起点
	d[st]=0;
	int h=1,en=2;
	q[h]=st;
	vis[st]=1;
	mvp[st]++;//入队次数++
	while(h<=en) {
		int v=q[h];//数组模拟队列,优化时间复杂度
		vis[v]=0;//标记此点
		for(int i=head[v]; i!=-1; i=e[i].next) {//访问相邻的点
			if(d[e[i].to]>d[v]+e[i].cs) {//松弛
				d[e[i].to]=d[v]+e[i].cs;
				if(vis[e[i].to]==0) {//没有进过队列
					q[en]=e[i].to;//放进队,变为队首
					en++;//队首指针后移
					mvp[e[i].to]++;//进队次数+1
					vis[e[i].to]=1;//标记
				}
				if(mvp[e[i].to]>n)return 1;//大于n就return true
			}
		}
		h++;//队尾指针后移
	}
	return 0;//没找到负环,return flase
}
int main() {
	cin>>f;
	for(int i=1; i<=f; i++) {
		memset(head,-1,sizeof(head));
		memset(e,0,sizeof(e));
		memset(mvp,0,sizeof(mvp));
		memset(q,0,sizeof(q));
		memset(vis,0,sizeof(vis));//多组数据,记得初始化
		num=1;
		for(int j=1; j<=99999; j++)d[j]=1e9;//初始化
		cin>>n>>m>>w;
		for(int j=1; j<=m; j++) {
			cin>>s>>e1>>t;
			add(s,e1,t);
			add(e1,s,t);
		}
		for(int j=1; j<=w; j++) {
			cin>>s>>e1>>t;
			t=t*-1;//因为这个虽然输入的是正数,实际上是负边权
			add(s,e1,t);
		}
		if(spfa(1)==1)cout<<"YES"<<endl;//spfa判断是否有负边权
		else cout<<"NO"<<endl;
	}
	return 0; 
} 

你可能感兴趣的:(C++的世界,题解,入门OJ)