【正环判断】POJ-1860 Currency Exchange(Dijkstra)

上链接:1860 -- Currency Exchange

大部分题解都是Bellmean_Ford和SPFA,最近在练Dijkstra的板,就放在这道题上写一写叭。

题意

给定n种货币,且存在m种交易方式,对于每一种交易方式,有税率与佣金等参数。例如现存在一种交易方式:从货币A到货币B,原有货币A的数量为Sa,税率为rate,佣金为dec,则可得货币B的数量为Sb=(Sa-dec)*rate。问经过交易能否使原有货币价格升高。

分析

由于允许的货币交易次数是无限的,故不能将视野局限于某几次交易能否使手中资本得到升高,而是去判断是否存在一种交易圈能够使得手中的资金不断升高。

对于这些货币以及货币间的交易方式,可以抽象成图的节点和边,每一条边连接两个节点,且存在税率与佣金等参数。那么要寻找到能使手中资金不断升高的交易圈,即在当前图中找到一个最大正环。这个概念其实是就和学习最短路算法时所提到的负圈判断一模一样,即在完成目前可进行的松弛操作(本题中是找到更长路)后,仍存在一些边(交易方式),使得手中资金仍然能够产生更大的价值

解题过程

存图

采用邻接表存图(真的感觉邻接表好好用,但是这一题数据量小到用邻接矩阵就可以存下?)注意本题实际上是给出一个有向图。

优先队列+Dijkstra

定义节点结构体,记录节点编号以及当前节点下能获得的最大货币价值,重载运算符使得优先队列优先弹出当前最大价值节点。(题板,不做赘述)

检查正环

检查所有边,若存在一条边使得经过这条边的交易方式后货币价值仍能够提高,则返回真,输出YES,反之即输出NO。

AC代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <math.h>
#include <bitset>
#include <algorithm>
#include <climits> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long int ll;
const int INF=0x3f3f3f3f;
const int N=500;
struct edge {
	int u,v;
	double rate,dec;
	edge(int a,int b,double c,double d) {
		u=a;v=b;rate=c;dec=d;
	}
};
vector<edge> G[N];//邻接表存图
struct s_node {
	int id;
	double val;
	s_node(int a,double b) {
		id=a;val=b;
	}
	//重载运算符
	bool operator<(const s_node &a) const {
		return val<a.val;
	}
};
int n,m,s;
double V;
bool dijkstra() {
	double dis[N];//记录所有点到起点能够产生的最大价值
	bool vis[N];
	for(int i=1;i<=n;i++) {
		//与最短路相反,初始化定义为0,即当前无价值
		dis[i]=0;
		vis[i]=false;
	}
	//对于起点,本存在有价值V
	dis[s]=V;
	priority_queue<s_node> q;
	q.push(s_node(s,dis[s]));
	while(!q.empty()) {
		//优先弹出目前已产生价值中有最大价值的节点
		s_node u=q.top();
		q.pop();
		if(vis[u.id]) continue;
		vis[u.id]=true;
		//检查从该节点出发的所有交易方式
		for(int i=0;i<G[u.id].size();i++) {
			//交易方式y
			edge y=G[u.id][i];
			if(vis[y.v]) continue;
			//通过y的交易之后若得到更大价值,则拓展新邻居
			if(dis[y.v]<(u.val-y.dec)*y.rate) {
				dis[y.v]=(u.val-y.dec)*y.rate;
				q.push(s_node(y.v,dis[y.v]));
			}
		}
	}
	//非常暴力地查每一条边,但应该只是看起来暴力,毕竟边数不变。
	for(int i=1;i<=n;i++) {
		for(int j=0;j<G[i].size();j++) {
			edge x=G[i][j];
			//若仍存在一种交易方式x,能够扩大价值,则返回true.
			if((dis[x.u]-x.dec)*x.rate>dis[x.v]) return true;
		}
	}
	return false;
}
void solve() {
	cin>>n>>m>>s>>V;
	for(int i=1;i<=m;i++) {
		int u,v;cin>>u>>v;
		double x,y;cin>>x>>y;
		G[u].push_back(edge(u,v,x,y));
		cin>>x>>y;
		G[v].push_back(edge(v,u,x,y));
	}
	if(dijkstra()) {
		cout<<"YES"<<endl;
	} else {
		cout<<"NO"<<endl;
	}
}
int main()
{
	//#define mytest
	#ifdef mytest
	   freopen("test.in","r",stdin);
	   freopen("test.out","w",stdout);
	#endif
	solve();
	return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值