*【POJ - 1860】Currency Exchange (单源最长路---Bellman_Ford算法判正环)

题干:

Description

Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and performs exchange operations only with these currencies. There can be several points specializing in the same pair of currencies. Each point has its own exchange rates, exchange rate of A to B is the quantity of B you get for 1A. Also each exchange point has some commission, the sum you have to pay for your exchange operation. Commission is always collected in source currency. 
For example, if you want to exchange 100 US Dollars into Russian Rubles at the exchange point, where the exchange rate is 29.75, and the commission is 0.39 you will get (100 - 0.39) * 29.75 = 2963.3975RUR. 
You surely know that there are N different currencies you can deal with in our city. Let us assign unique integer number from 1 to N to each currency. Then each exchange point can be described with 6 numbers: integer A and B - numbers of currencies it exchanges, and real RAB, CAB, RBA and CBA - exchange rates and commissions when exchanging A to B and B to A respectively. 
Nick has some money in currency S and wonders if he can somehow, after some exchange operations, increase his capital. Of course, he wants to have his money in currency S in the end. Help him to answer this difficult question. Nick must always have non-negative sum of money while making his operations. 

Input

The first line of the input contains four numbers: N - the number of currencies, M - the number of exchange points, S - the number of currency Nick has and V - the quantity of currency units he has. The following M lines contain 6 numbers each - the description of the corresponding exchange point - in specified above order. Numbers are separated by one or more spaces. 1<=S<=N<=100, 1<=M<=100, V is real number, 0<=V<=103. 
For each point exchange rates and commissions are real, given with at most two digits after the decimal point, 10-2<=rate<=102, 0<=commission<=102. 
Let us call some sequence of the exchange operations simple if no exchange point is used more than once in this sequence. You may assume that ratio of the numeric values of the sums at the end and at the beginning of any simple sequence of the exchange operations will be less than 104. 

Output

If Nick can increase his wealth, output YES, in other case output NO to the output file.

Sample Input

3 2 1 20.0
1 2 1.00 1.00 1.00 1.00
2 3 1.10 1.00 1.10 1.00

Sample Output

YES

题目大意:

有多种汇币,汇币之间可以交换,这需要手续费,当你用100A币交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到(100 - 0.39) * 29.75 = 2963.3975 B币。问s币的金额经过交换最终得到的s币金额数能否增加

货币的交换是可以重复多次的,所以我们需要找出是否存在正权回路,且最后得到的s金额是增加的

怎么找正权回路呢?(正权回路:在这一回路上,顶点的权值能不断增加即能一直进行松弛)

解题报告:

一种货币就是一个点

一个“兑换点”就是图上两种货币之间的一个兑换方式,是双边,但A到B的汇率和手续费可能与B到A的汇率和手续费不同。

唯一值得注意的是权值,当拥有货币A的数量为V时,A到A的权值为K,即没有兑换

而A到B的权值为(V-Cab)*Rab

本题是“求最大路径”,之所以被归类为“求最小路径”是因为本题题恰恰与bellman-Ford算法的松弛条件相反,求的是能无限松弛的最大正权路径,但是依然能够利用bellman-Ford的思想去解题。

因此初始化dis(S)=V   而源点到其他点的距离(权值)初始化为无穷小(0),当s到其他某点的距离能不断变大时,说明存在最大路径;如果可以一直变大,说明存在正环。判断是否存在环路,用Bellman-Ford和spfa都可以。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n,m,s;
double v;
int cnt;
double dis[300 + 5];
struct Point {
	int pos,to;
	double rate,cost;
	Point(){}
	Point(int pos,int to,double rate,double cost):pos(pos),to(to),rate(rate),cost(cost){}
} p[300 + 5];

bool Bellman_ford() {
 //此处与Bellman-Ford的处理相反,初始化为源点到各点距离0,到自身的值为原值
    dis[s] = v;
	//如果有一遍跑的时候,一个点也没松弛,那就说明没正环了,直接break。 
	int flag = 0;
	for(int i = 1; i<n; i++) {
		flag = 0;
		for(int j = 0; j<cnt; j++) {
			if( ( dis[p[j].pos] - p[j].cost )*p[j].rate > dis[p[j].to] ) {
				dis[p[j].to] = (dis[p[j].pos]-p[j].cost)*p[j].rate;
				flag = 1;
			} 
		}	
		if(!flag ) {
			return 0;//说明一遍下来一个可松弛的点都没有,说明没正环。 
		}
	}
	//再跑一遍 
	flag = 0;
	for(int j = 0; j<cnt; j++) {
		if( ( dis[p[j].pos] - p[j].cost )*p[j].rate > dis[p[j].to] ) {
			dis[p[j].to] = (dis[p[j].pos]-p[j].cost)*p[j].rate;
			return 1;//找到了可松弛的点 说明有正环 
		} 
	}
	if(flag == 0)   //这句应该不能加? 
	return 0 ;//反之就没正环	
}
void init() {
	cnt = 0;
	memset(p,0,sizeof(p));
	memset(dis,0,sizeof(dis));
}
int main()
{
	//好像不是多组输入? 
	double r1,r2,c1,c2;
	while(~scanf("%d%d%d%lf",&n,&m,&s,&v) ) {
//		printf("%d",v);
		init();
		int a,b;
		while(m--) {
			scanf("%d%d%lf%lf%lf%lf",&a,&b,&r1,&c1,&r2,&c2);
			p[cnt] = Point(a,b,r1,c1);
			cnt++;
			p[cnt] = Point(b,a,r2,c2);
			cnt++;
		}
//		for(int i = 0; i<cnt; i++) {
//			printf("%d  %d  %lf   %lf\n",p[i].pos,p[i].to,p[i].rate,p[i].cost);			
//		} 
//		printf("%d\n",cnt);
		if(Bellman_ford()) printf("YES\n");
		else printf("NO\n");
	}
	
	
	return 0 ;
 } 

AC代码2(邻接表,还未看) 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#define LL long long
#define eps 1e-8
#define maxn 310
#define inf 0x3f3f3f3f
using namespace std;

int sign(double x){
    if(fabs(x)<eps) return 0;
    return x<0? -1:1;
}

int m,n,s;
double cur;
int edges, u[maxn], v[maxn];
double rate[maxn], cost[maxn];
int first[maxn], next[maxn];
//初始化edge和first
double dis[maxn];

void add_edge(int s, int t, double a, double b) {
    u[edges] = s; v[edges] = t;
    rate[edges] = a; cost[edges] = b;
    next[edges] = first[s];
    first[s] = edges++;
}

bool bellman(int s) {
    for(int i=1; i<=n; i++) dis[i] = -1;
    dis[s] = cur;  //!!!

    for(int i=1; i<=n; i++) {
        for(int e=0; e<edges; e++) {
            double tmp = (dis[u[e]]-cost[e])*rate[e];
            if(sign(dis[v[e]]-tmp) < 0) {
                dis[v[e]] = tmp;
                if(i == n) return 0;
            }
        }
    }

    return 1;
}

int main(int argc, char const *argv[])
{

    while(scanf("%d %d %d %lf", &n,&m,&s,&cur) != EOF)
    {
        memset(first, -1, sizeof(first));
        edges = 0;

        for(int i=1; i<=m; i++) {
            int u,v; double ra,rb,ca,cb;
            scanf("%d %d %lf %lf %lf %lf", &u,&v,&ra,&ca,&rb,&cb);
            add_edge(u,v,ra,ca);
            add_edge(v,u,rb,cb);
        }

        if(!bellman(s)) puts("YES");
        else puts("NO");
    }

    return 0;
}

 

总结:

    刚开始一直调试不出正确答案,scanf的时候c2写成c1了,而且在main函数中定义了int u,v本来是想读入Point的起点终点,但是误打误撞有个全局变量double v, 导致一直出错误答案。

全部评论

相关推荐

2025-12-25 10:16
已编辑
合肥工业大学 后端工程师
如题,在历经了长达多月的焦急等待,楼主也算是如愿以偿收到了梦中情司的意向了,秋招也终于是落下了帷幕,虽然手中的offer不够打牌,但已经满足了。华为时间线:9.3&nbsp;笔试环节,惊险通过10.15&nbsp;线下面试,前两轮技术面手撕都比较轻松,面试官态度也很好,最后一轮主管面,向主管表达了强烈的意愿,主管很和蔼,面试体验非常棒,1125定律后入池成功11.19&nbsp;收到接口人的保温电话12.9&nbsp;接到部门hr的保温电话,介绍了一下部门负责的工作12.23&nbsp;收到华为的意向书,成为华孝子一枚~期间收到了之前实习过的公司的offer,害怕华子泡不出来就先签三方了,这下不得不毁约了,在此向前司道个歉,也感谢前司对我的认可和托举,祝业务蒸蒸日上~感谢从今年三月开始找暑期实习以来,所有朋友和家人的鼓励,我们宿舍的就业氛围相当好,大家会分享各种有用的信息以及面试中遇到刁钻的面试题,在有人收到offer的时候我们都会发自内心的高兴和祝福,在我去线下面的时候也借我穿过西服.....能在大学四年分入这么好的宿舍拥有这么这么好的舍友除了幸运我找不出其他的形容词。还要感谢我的父母,在我每一次面试前都给予鼓励,也在失败的时候安慰我,他们的托底是我前进的基石,以后有工资了要给父母买很多东西最感谢的是我的女朋友,我们从大一相识,一直坚持到大四,她是一个非常优秀也异常坚定的女生,也正是因为她的实力出众早再年初就定好了要去上海的一家外企。我为了也去上海,从暑期实习开始投了不少上海的岗位但无一例外的都被拒之门外,但这期间她从来没有嫌弃过我,反而一直鼓励我相信我,如果说父母的托底是我前进的基石,那女朋友的鼓励和信任则是我前进的动力和方向。在如今这个充满戾气和对立的社会,能找到一个一心一意彼此喜欢的人实在是很难得,我深知这种珍贵所以更会加倍珍惜也感谢自己吧,在经历了无数个失眠的夜晚和面试失败的打击下,最终还是迎来了最好的结果,记得在华为线下面的前几周我几乎回到了高三时期的作息,那真是一段充实美好的时光,好在最后的结果也没有辜负这份努力也想跟所有的牛友说:不要因为一时的失败而自怨自艾,妄自菲薄,只要坚持下去,总会有柳暗花明又一村的惊喜在等待着你,机会总是垂青于有准备的人,要相信否极泰来,相信自己。朋友,坚定地相信未来吧,相信不屈不挠的努力,相信战胜死亡的年轻,相信未来、热爱生命。
小肥罗:有这样的女朋友真是幸福
秋招白月光
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务