3259 :Wormholes:spfa+bellman-ford如何判断负环

题目翻译

在探索他的许多农场时,农夫约翰发现了许多惊人的虫洞。 虫洞非常特殊,因为它是一条单向路径,可在您进入虫洞之前将您送至目的地! FJ的每个农场均包含N(1≤N≤500)个田地,方便地编号为1…N,M(1≤M≤2500)条路径和W(1≤W≤200)虫洞。

由于FJ是一位狂热的时空旅行爱好者,因此他希望做到以下几点:从某个领域开始,经过一些路径和虫洞,并在他初次离开之前的某个时间返回开始领域。 也许他将能够见到自己:)。

为了帮助FJ查明是否可行,他将向您提供其农场F(1≤F≤5)的完整地图。 路径行驶时间不会超过10,000秒,虫洞也不会使FJ的返回时间超过10,000秒。

输入输出

第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的单向路径也使旅行者退回T秒。

思路分析

起始点是没有给定的,也就是说我们从任何一点开始,只要存在能回到自身的权重之和为负的路径即可。
两种方法:spfa或者floyd,floyd显然时间复杂度更高但是这道题里也是可以用的:

Tips:

  • 农场与农场之间路径是双向的。
  • 两个位置链接的路径不止一条,需要去重

floyd

对于floyd算法而言,有负环也就意味着dp[i][i]至少有一个小于0.注意这里面我们不再使用min函数,而是使用《判断再赋值。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int map[2005][2005]={0};
int main()
{
	int n,m,s;
	int t;
	scanf("%d",&t);
	getchar();
	while(t--)
	{
		scanf("%d%d%d",&n,&m,&s);
//		printf("%d  %d  %d\n",n,m,s);
		for(int i=1;i<=n;i++)
		{
			map[i][i]=0;
			for(int j=1;j<=n;j++)
			{
				map[i][j]=inf;
			}
		}
		int u,v,w;
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			if(map[u][v]>w)
			map[u][v]=map[v][u]=w;
		}
		for(int i=0;i<s;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			map[u][v]=-w;
		}
		
		int flag=0;
		for(int k=1;k<=n;k++)
		{
			for(int i=1;i<=n;i++)
			{
				for(int j=1;j<=n;j++)
				{
					if(map[i][j]>map[i][k]+map[k][j])
						map[i][j]=map[i][k]+map[k][j];
				}
				if(map[i][i]<0)
				{
					flag=1;
					break;
				}
			}
			if(flag==1)
			break;
		}
		if(flag==1)
			printf("YES\n");
		else
			printf("NO\n");
	}
}

在这里插入图片描述
效果不是特别好,那么我们如果使用bellman-ford的升级版。spfa呢?

SPFA

对于spfa而言,我们可以随意指定一个初始点s,如果他经过的点数大于n个,显然就是存在负环了。

#include <iostream>
#include<vector>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;

#define inf 10000000
#define MAX 2005 //农场+虫洞

struct edge {
	int to, cost;
	edge(int a, int b) { to = a, cost = b; }
};

vector<edge> G[MAX];
int vis[MAX], d[MAX];
int main()
{
	int f;
	cin >> f;
	while (f--) {
		int n, m, w, a, b, c;
		cin >> n >> m >> w;
		memset(vis, 0, sizeof(vis));
		fill(d, d + MAX, 0);
		for (int i = 1; i <= n; i++)G[i].clear();
		for (int i = 0; i < m; i++) {
			cin >> a >> b >> c;
			G[a].push_back(edge(b, c));
			G[b].push_back(edge(a, c));
		}
		for (int i = 0; i < w; i++) {
			cin >> a >> b >> c;
			G[a].push_back(edge(b, -c));
		}

		int cnt = 0, sign = 1;
		for (int i = 0; i < n && sign; i++) {//不断的更新
			for (int j = 1; j <= n && sign; j++) {
				for (int k = 0; k < G[j].size(); k++) {
					edge e = G[j][k];
					if (d[e.to] > d[j] + e.cost) {
						d[e.to] = d[j] + e.cost;
						if (i == n - 1) { sign = 0; break; }//第n次更新最短路径都有变化
					}
				}
			}
		}
		if (sign)cout << "NO" << endl;
		else cout << "YES" << endl;
	}
}

在这里插入图片描述
自己对图论还是不熟啊,准备写一篇超长总结了~~

发布了186 篇原创文章 · 获赞 13 · 访问量 9284

猜你喜欢

转载自blog.csdn.net/csyifanZhang/article/details/105266973
今日推荐