POJ - 1860判断正环

题目链接
题意
有n种类型的货币,m个城市可以兑换两种货币,一个人手上有v块s类型的钱,要求判断是否可以经过若干次兑换后这个人手上的s类型的钱增加了。
思路
对于每种货币,建立一个点,两种货币可以交换,则这两个点之间有一条边, 经过若干次兑换后要回到s类型的钱,则一定存在环,而钱要增加,则需要存在正权环,所以用通过上述方法建图,然后跑spfa,注意初始化d数组要初始化为-INF,并且松弛条件改为(d[y] < (d[x] - c[i]) * edge[i])。也就是能使钱增加的判断条件。

#include <iostream>
#include <cstdio>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const ll MAX = 1e9;
const ll MAXN = 1e3 + 10;
int head[MAXN], to[MAXN], nex[MAXN], cnt;//链式前向星
double edge[MAXN], c[MAXN];
int vis[MAXN], num[MAXN];
double d[MAXN];
void init()
{
	memset(head, -1, sizeof(head));
	cnt = 0;
}
void add(int x, int y, double c1, double c2)
{
	to[cnt] = y;
	edge[cnt] = c1;
	c[cnt] = c2;
	nex[cnt] = head[x];
	head[x] = cnt++;
}
bool spfa(int s, int n, double sum)
{
	queue<int> que;
	for (int i = 1; i <= n; i++) d[i] = -INF;//初始化*
	memset(vis, 0, sizeof(vis));
	memset(num, 0, sizeof(num));
	d[s] = sum;//*
	vis[s] = 1, num[s]++;
	que.push(s);
	while (!que.empty())
	{
		ll x = que.front();
		que.pop();
		vis[x] = 0;
		for (int i = head[x]; ~i; i = nex[i])//遍历x结点相连的边
		{
			ll y = to[i];
			if(d[y] < (d[x] - c[i]) * edge[i] )
			{
			//由于更新了节点,所以后续以这个为基础的最短路,也要更新下
			//所以如果在队列就不用加入,不在的话加入更新后续节点
				d[y] = (d[x] - c[i]) * edge[i];
				if(!vis[y])
				{
					que.push(y);
					vis[y] = 1;
					num[y]++;
					if(num[y] >= n)//如果有顶点进入队列超过n -1 次,则有正权环
						return true;
				}
			}
		}
	}
	return false;
}
int main()
{
	int n, m, s;
	double v;
	init();
	cin >> n >> m >> s >> v;
	for (int i = 1; i <= m; i++) {
        int x, y;
        double x1, x2, x3, x4;
        cin >> x >> y >> x1 >> x2 >> x3 >> x4;
        add(x, y, x1, x2);
        add(y, x, x3, x4);
	}
	if(spfa(s, n, v)) printf("YES\n");
	else printf("NO\n");
	return 0;
}

发布了26 篇原创文章 · 获赞 2 · 访问量 414

猜你喜欢

转载自blog.csdn.net/D_Bamboo_/article/details/103515065