POJ 3259 Wormholes(bellman_ford、Floyd、SPFA判断负环)

POJ 3259 http://poj.org/problem?id=3259

题意:

农夫 FJ 有 N 块田地【编号 1...n】 (1<=N<=500)
田地间有 M 条路径 【双向】(1<= M <= 2500)
同时有 W 个孔洞,可以回到以前的一个时间点【单向】(1<= W <=200)
问:FJ 是否能在田地中遇到以前的自己

算法:bellman_ford 判断是否有负环

思路:

田地间的双向路径加边,权值为正
孔洞间的单向路径加边,权值为负【可以回到以前】
判断有向图是否存在负环
因为如果存在了负数环,时间就会不停的减少,
那么 FJ 就可以回到以前更远的地方,肯定能遇到以前的自己的

bellman_ford

#ifndef ONLINE_JUDGE
#pragma warning(disalbe : 4996)
#endif
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 510;
const int maxw = 2500 * 2 + 200 + 10;
const int inf = 10000;
int d[maxn];
int n, m;

struct Edge {
	int u, v;
	int t;
}edge[maxw];

bool bellman_ford() {
	for (int i = 1; i <= n; ++i) d[i] = inf;
	d[1] = 0;//起点定为0

	for (int i = 1; i < n; ++i) {
		bool flag = true;
		for (int j = 0; j < m; ++j) {
			int u = edge[j].u;
			int v = edge[j].v;
			int t = edge[j].t;

			if (d[v] > d[u] + t) {//松弛操作
				d[v] = d[u] + t;
				flag = false;
			}
		}
		if (flag) return false;//如果当前轮不能松弛,直接判断没有负数环
	}

	for (int i = 0; i < m; ++i) 
		if (d[edge[i].v] > d[edge[i].u] + edge[i].t)
			return true;//如果仍然能够松弛则存在负环
	
	return false;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif // !ONLINE_JUDGE
	int T;
	int M, W;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &n, &M, &W);
		m = 0;

		int u, v, t;
		for (int i = 1; i <= M; i++) //田地间的大路,加双边
		{
			scanf("%d%d%d", &u, &v, &t);
			edge[m].u = u;
			edge[m].v = v;
			edge[m++].t = t;

			edge[m].u = v;
			edge[m].v = u;
			edge[m++].t = t;
		}

		for (int i = 1; i <= W; i++) //孔洞,加单边
		{
			scanf("%d%d%d", &u, &v, &t);
			edge[m].u = u;
			edge[m].v = v;
			edge[m++].t = -t;
		}

		if (bellman_ford()) printf("YES\n"); //存在负数环
		else printf("NO\n");
	}
#ifndef ONLINE_JUDGE
	fclose(stdin);
	fclose(stdout);
	system("out.txt");
#endif // !ONLINE_JUDGE
	return 0;
}

Floyd

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
using namespace std;

int n, m, W;//教室、普通走廊、回溯
const int inf = 0x3f3f3f3f, maxn = 510;
int g[maxn][maxn];

bool floyd() {
	for (int k = 1; k <= n; ++k)
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j)
				if (g[i][j] > g[i][k] + g[k][j])
					g[i][j] = g[i][k] + g[k][j];
			if (g[i][i] < 0)return true;//存在负环
		}
	return false;
}

int main() {
	//freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t; cin >> t; while (t--) {
		cin >> n >> m >> W;
		for (int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)
				g[i][j] = inf;
		for (int i = 0; i <= n; ++i)g[i][i] = 0;

		int u, v, w;
		for (int i = 0; i < m; ++i)
			cin >> u >> v >> w, g[u][v] = g[v][u] = min(g[u][v],w);
		for (int i = 0; i < W; ++i)
			cin >> u >> v >> w, g[u][v] = -w;
		cout << (floyd() ? "YES" : "NO" )<< endl;
		
	}
}

SPFA

//kuangbin 的板子确实挺漂亮的
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
int n;
const int MAXN = 505, 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) {
    memset(vis, false, sizeof vis);
    memset(cnt, 0, sizeof cnt);
    memset(dist, INF, sizeof dist);
    queue<int> que;
    que.push(start);
    ++cnt[start];
    dist[start] = 0;
    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 true;
                }
            }
        }
    }
    return false;
}

int main() {
    int f, m, W;
    scanf("%d", &f);
    while (f--) {
        scanf("%d%d%d", &n, &m, &W);
        int u, v, w;
        for (int i = 0; i < m; ++i) {
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w);
            addedge(v, u, w);
        }
        for (int i = 0; i < W; ++i) {
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, -w);
        }

        printf("%s\n", SPFA(1) ? "YES" : "NO");
        for (int i = 1; i <= n; ++i)
            E[i].clear();
    }
    return 0;
}

三种方法AC以后发现BF竟然时间最短

posted @ 2020-07-25 17:29  RioTian  阅读(336)  评论(0编辑  收藏  举报