POJ 3259 Wormholes(bellman_ford,判断有没有负环回路)

题意:John的农场里field块地,path条路连接两块地,hole个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。我们的任务是知道会不会在从某块地出发后又回来,看到了离开之前的自己。

思路:

这题就是判断存不存在负环回路。

前M条是双向边,后面的W是单向的负边。

为了防止出现不连通,增加一个结点作为起点。起点到所有点的长度为0

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
/*
* 单源最短路bellman_ford算法,复杂度O(VE)
* 可以处理负边权图。
* 可以判断是否存在负环回路。返回true,当且仅当图中不包含从源点可达的负权回路
* vector<Edge>E;先E.clear()初始化,然后加入所有边
* 点的编号从1开始
*/
const int INF = 0x3f3f3f3f;
const int MAXN = 550;
int dist[MAXN];
struct Edge
{
	int u, v;
	int cost;
	Edge(int _u = 0, int _v = 0, int _cost = 0) :u(_u), v(_v), cost(_cost){}
};
vector<Edge> E;
bool bellman_ford(int start, int n)//点的编号从1开始
{
	for (int i = 1; i <= n; i++)dist[i] = INF;
	dist[start] = 0;
	for (int i = 1; i<n; i++)//最多做n-1次
	{
		bool flag = false;
		for (int j = 0; j<E.size(); j++)
		{
			int u = E[j].u;
			int v = E[j].v;
			int cost = E[j].cost;
			if (dist[v]>dist[u] + cost)
			{
				dist[v] = dist[u] + cost;
				flag = true;
			}
		}
		if (!flag)return true;//没有负环回路
	}
	for (int j = 0; j<E.size(); j++)
	if (dist[E[j].v]>dist[E[j].u] + E[j].cost)
		return false;//第n次更新则有负环回路
	return true;//没有负环回路
}

int main()
{
	int T;
	int N, M, W;
	int a, b, c;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &N, &M, &W);
		E.clear();
		while (M--)
		{
			scanf("%d%d%d", &a, &b, &c);
			E.push_back(Edge(a, b, c));
			E.push_back(Edge(b, a, c));
		}
		while (W--)
		{
			scanf("%d%d%d", &a, &b, &c);
			E.push_back(Edge(a, b, -c));
		}
		for (int i = 1; i <= N; i++)
			E.push_back(Edge(N + 1, i, 0));
		if (!bellman_ford(N + 1, N + 1))printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

(2) 如果一开始对所有顶点i,都把dist[i]初始化为0,那么可以检查出所有的负圈

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
/*
* 单源最短路bellman_ford算法,复杂度O(VE)
* 可以处理负边权图。
* vector<Edge>E;先E.clear()初始化,然后加入所有边
* 点的编号从1开始
*/
const int INF = 0x3f3f3f3f;
const int MAXN = 550;
int dist[MAXN];
struct Edge
{
	int u, v;
	int cost;
	Edge(int _u = 0, int _v = 0, int _cost = 0) :u(_u), v(_v), cost(_cost){}
};
vector<Edge> E;
bool bellman_ford(int n)//点的编号从1开始
{
	for (int i = 1; i <= n; i++)dist[i] = 0;
	for (int i = 1; i<n; i++)//最多做n-1次
	{
		bool flag = false;
		for (int j = 0; j<E.size(); j++)
		{
			int u = E[j].u;
			int v = E[j].v;
			int cost = E[j].cost;
			if (dist[v]>dist[u] + cost)
			{
				dist[v] = dist[u] + cost;
				flag = true;
			}
		}
		if (!flag)return true;//没有负环回路
	}
	for (int j = 0; j<E.size(); j++)
	if (dist[E[j].v]>dist[E[j].u] + E[j].cost)
		return false;//第n次更新则有负环回路
	return true;//没有负环回路
}

int main()
{
	int T;
	int N, M, W;
	int a, b, c;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &N, &M, &W);
		E.clear();
		while (M--)
		{
			scanf("%d%d%d", &a, &b, &c);
			E.push_back(Edge(a, b, c));
			E.push_back(Edge(b, a, c));
		}
		while (W--)
		{
			scanf("%d%d%d", &a, &b, &c);
			E.push_back(Edge(a, b, -c));
		}
		if (!bellman_ford(N))printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

(3) SPFA

某个顶点进入队列的次数超过N,则有负环

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
/*
* 单源最短路SPFA
*/
const int MAXN = 1010;
const int INF = 0x3f3f3f3f;
struct Edge
{
	int v;
	int cost;
	Edge(int _v = 0, int _cost = 0) :v(_v), cost(_cost){}
};

vector<Edge> E[MAXN];

void addedge(int u, int v, int w)
{
	E[u].push_back(Edge(v, w));
}

bool vis[MAXN];
int cnt[MAXN];
int dist[MAXN];

bool SPFA(int start, int n)
{
	memset(vis, false, sizeof(vis));
	for (int i = 1; i <= n; i++)dist[i] = INF;
	dist[start] = 0;
	vis[start] = true;
	queue<int>que;
	while (!que.empty())que.pop();
	que.push(start);
	memset(cnt, 0, sizeof(cnt));
	cnt[start] = 1;
	while (!que.empty())
	{
		int u = que.front();
		que.pop();
		vis[u] = false;
		for (int i = 0; i<E[u].size(); i++)
		{
			int v = E[u][i].v;
			if (dist[v]>dist[u] + E[u][i].cost)
			{
				dist[v] = dist[u] + E[u][i].cost;
				if (!vis[v])
				{
					vis[v] = true;
					que.push(v);
					if (++cnt[v]>n)return false;
					//有负环回路
				}
			}
		}
	}
	return true;
}
int main()
{
	int T;
	int N, M, W;
	int a, b, c;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &N, &M, &W);
		for (int i = 1; i <= N + 1; i++)E[i].clear();
		while (M--)
		{
			scanf("%d%d%d", &a, &b, &c);
			addedge(a, b, c);
			addedge(b, a, c);
		}
		while (W--)
		{
			scanf("%d%d%d", &a, &b, &c);
			addedge(a, b, -c);
		}
		for (int i = 1; i <= N; i++)
			addedge(N + 1, i, 0);
		if (!SPFA(N + 1, N + 1))printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}
posted @ 2017-08-18 13:52  demianzhang  阅读(559)  评论(0编辑  收藏  举报