Poj 1860 Currency Exchange 【spfa算法如何判断负权环】

负权环的判断方法

根据松弛次数

n代表节点个数。

根据松弛次数是否大于等于n来判断负权环,是从网上其他博客说的,根据出队次数是否大于等于n来判断,想到的。因为,判断出队次数,是判断更新次数的上界。

用一个n大小的数组来代表每个点的松弛次数。因为SPFA算法里的松弛,和Bellman-ford算法里的松弛一样。Bellman-ford算法里,对同一个点的松弛次数,在极端情况下,可以想象把这些松弛次数分配到每一次迭代求解中去,而迭代求解一共只有n-1次。所以一旦某个点的松弛次数等于了n,那么就说明有负环。

所以,在每次进行松弛后,遍历判断每个点的松弛次数,如果有一个等于n(再执行下去就会大于n),就说明有负环

题目解析

题目链接,这道题还是比较有意思的,以每种货币为节点,一个兑换所提供两条边,我们需要找到这个图中是否有一个环使得我们的money可以无限增多,从负环到这里的变化,无非是改变了松弛条件,建好图,利用上述的性质便可得到。

#define ll long long
#define vec vector<int>
#define P pair<int,int>
#define MAX 105

int N, M, S, A, B, vis[MAX], num[MAX];
double V, c, a1, a2, a3, a4, mon[MAX];

struct edge {
	int to; double rate, cost;
	edge(int a = 0, double b = 0, double c = 0) { to = a, rate = b, cost = c; }
};
vector<edge> G[MAX];

void addEdge(int a,int b, double a1,double a2, double a3, double a4) {
	G[a].push_back(edge(b, a1, a2));
	G[b].push_back(edge(a, a3, a4));
}

void spfa() {
	memset(mon, 0, sizeof(mon));
	memset(vis, 0, sizeof(vis));
	memset(num, 0, sizeof(num));
	queue<int> q; q.push(S);
	mon[S] = V; vis[S] = 1;
	int cnt = -1;
	while (!q.empty()) {
		int u = q.front(); vis[u] = 0; q.pop(); num[u]++;
		if (num[u] > N) { cnt = u; break; };//存在一条可以不断增加money的路
		//注意这里是先num++的,因此大于N+1时才结束
		for (int i = 0; i < G[u].size(); i++) {
			edge e = G[u][i];
			if ((mon[u] - e.cost)*e.rate > mon[e.to]) {
				mon[e.to] = (mon[u] - e.cost)*e.rate;
				if (!vis[e.to]) {
					q.push(e.to);	
					vis[e.to] = 1;
				}
			}
		}
	}
	if (cnt == -1)cout << "NO" << endl;
	else cout << "YES" << endl;
}

int main() {
	while (cin >> N >> M >> S >> V) {
		//以钱币的种类为点
		for (int i = 1; i <= N; i++)G[i].clear();
		for (int i = 1; i <= M; i++) {
			cin >> A >> B >> a1 >> a2 >> a3 >> a4;
			addEdge(A, B, a1, a2, a3, a4);
		}
		spfa();
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章