poj_1860 SPFA

题目大意

有N种货币,M个兑换点,每个兑换点只能相互兑换两种货币。设兑换点A可以兑换货币C1和C2,给出rate(C1--C2)表示1单位的C1货币可以兑换出的C2货币数目,rate(C2--C1)表示1单位的C2货币可以兑换出的C1货币数目,commision(C1)表示用C1兑换C2的时候兑换点需要收取的佣金,commison(C2)表示C2到C1兑换时候兑换点收取的佣金,那么进行C1-C2的兑换操作实际得到的结果为 (amount(C1)-commision(C1))*rate(C1-C2),同理C2-C1的兑换操作最终得到的C1为 (amount(C2)-commision(C2))*rate(C2-C1)。 
    先给定数目为V的货币S,问是否有一种兑换方法,使得经过兑换之后,S的数目增加。

题目分析

要求从货币S经过若干次兑换,最终回到S,使得S的数目增加。将N种货币视为N个点,将M个兑换点视为M条双向路径,则题目转换为是否存在一条回路使得从点S出发回到S,初始值增加。 
    可以求出从S到每种货币经过路径兑换之后所能得到的最大数值,类似求单源最短路径,题目的结果就是从S到S的最大数值是否大于初始值V。 
    采用SPFA算法求解。由于图中的任意一条边都是双向的,且其中的回路可以走任意多次(回路上的货币可能一直增加),则如果存在使得soruce货币增加的回路,回路上的点会被push进队列,则一定能够回到source节点;如果不存在使得source货币增加的路径,则肯定不存在一直循环增加的回路,那么队列肯定不会一直增长不会陷入死循环;判断source到source的环是否使得钱增加即可,若增加则直接返回true

实现(c++)

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;

struct Edge{
	int vertex;
	double commision;
	double rate;
};
vector<vector<Edge> > gEdges;

double gCurrency[105];	//将每种货币视为图上的一个节点,gCurrency用于存放每个节点的钱数

bool Spfa(int source, int n, double init_value){
	queue<int> Q;
	Q.push(source);
	for (int i = 0; i <= n; i++){
		gCurrency[i] = 0;
	}
	gCurrency[source] = init_value;		//SPFA算法
	while (!Q.empty()){
		int v = Q.front();
		Q.pop();

		for (int i = 0; i < gEdges[v].size(); i++){
			Edge& e = gEdges[v][i];

			double tmp = (gCurrency[v] - e.commision)*e.rate;
			if (gCurrency[e.vertex] < tmp){
				gCurrency[e.vertex] = tmp;

				Q.push(e.vertex);

				//由于图中的任意一条边都是双向的,且其中的回路可以走任意多次(回路上的货币可能一直增加),
				//则如果存在使得soruce货币增加的回路,回路上的点会被push进队列,则一定能够回到source节点。

				//如果不存在使得source货币增加的路径,则肯定不存在一直循环增加的回路,那么队列肯定不会一直增长
				//不会陷入死循环

				//判断source到source的环是否使得钱增加即可,若增加则直接返回true
				if (e.vertex == source && gCurrency[source] > init_value){
					return true;
				}
			}
		}
	}
	return false;
}

int main(){
	int n, m, s;
	double v;
	scanf("%d %d %d %lf", &n, &m, &s, &v);
	int currency1, currency2;
	double commision1, commision2, rate1, rate2;
	gEdges.resize(105);
	Edge e;
	//初始化图结构
	for (int i = 0; i < m; i++){
		scanf("%d %d %lf %lf %lf %lf", &currency1, &currency2, &rate1, &commision1, &rate2, &commision2);
		e.vertex = currency2;
		e.commision = commision1;
		e.rate = rate1;
		gEdges[currency1].push_back(e);

		e.vertex = currency1;
		e.commision = commision2;
		e.rate = rate2;
		gEdges[currency2].push_back(e);
	}
	if (Spfa(s, n, v)) //用SPFA判断,从source到source的路径中有没有使得钱数增加的
		printf("YES\n");
	else
		printf("NO\n");
	return 0;
}
时间: 2024-10-13 11:27:27

poj_1860 SPFA的相关文章

UESTC30-最短路-Floyd最短路、spfa+链式前向星建图

最短路 Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的T-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗? Input 输入包括多组数据. 每组数据第一行是两个整数NN ,MM (N≤100N≤100 ,M≤10000M≤1000

畅通project续HDU杭电1874【dijkstra算法 || SPFA】

http://acm.hdu.edu.cn/showproblem.php?pid=1874 Problem Description 某省自从实行了非常多年的畅通project计划后.最终修建了非常多路.只是路多了也不好,每次要从一个城镇到还有一个城镇时,都有很多种道路方案能够选择,而某些方案要比还有一些方案行走的距离要短非常多.这让行人非常困扰. 如今,已知起点和终点,请你计算出要从起点到终点.最短须要行走多少距离. Input 本题目包括多组数据.请处理到文件结束. 每组数据第一行包括两个正

HDU 2722 Here We Go(relians) Again (spfa)

Here We Go(relians) Again Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 1   Accepted Submission(s) : 1 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description The Gorelians

[BZOJ 1295][SCOI2009]最长距离(SPFA+暴力)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1295 分析:很巧妙的一道spfa从搜索的角度是搜索在所有1中搜索删除哪T个1,对整个图询问,这样肯定TLE 不妨反过来想一想:对于两个点,弄出联通这两个点所需删除的最少的1,那么就知道这两个点是否可以作为题目要求的起点和终点,如果满足算一下结果和ans比较一下就可以. 所以算法就出来了: 枚举起点(S,T),用SPFA跑出图上的所有点到起点这条路径联通的最少删除的1,那么ans=max(di

poj3268 Silver Cow Party (SPFA求最短路)

其实还是从一个x点出发到所有点的最短路问题.来和回只需分别处理一下逆图和原图,两次SPFA就行了. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<map> #include<set> #include<vector> #

POJ--3259--Wormholes【SPFA判负权值回路】

题意:有n个点,之间有m条双向路径,还有w个虫洞,单向,从一点到另一点需要花费时间,但是有虫洞的话会减少时间,一个人想要走某一条路使得他能碰到过去的自己,问这个图是否能让他实现他的想法. 其实就是判一个图是否存在负权值回路,SPFA可以实现,原理是:如果存在负权值回路,那么从源点到某个顶点的距离就可以无限缩短,因此就会无限入队,所以在SPFA中统计每个顶点的入队次数,如果超过了n个(顶点个数)则说明存在负权值回路. 我把输出yes和输出no写反了,WA了两发,看了半天都没发现... #inclu

POJ 3259 Wormholes SPFA算法题解

本题其实也可以使用SPFA算法来求解的,不过就一个关键点,就是当某个顶点入列的次数超过所有顶点的总数的时候,就可以判断是有负环出现了. SPFA原来也是可以处理负环的. 不过SPFA这种处理负环的方法自然比一般的Bellman Ford算法要慢点了. #include <stdio.h> #include <string.h> #include <limits.h> const int MAX_N = 501; const int MAX_M = 2501; const

ZOJ 3794 Greedy Driver spfa

题意: 给定n个点,m条有向边,邮箱容量. 起点在1,终点在n,开始邮箱满油. 下面m行表示起点终点和这条边的耗油量(就是长度) 再下面给出一个数字m表示有P个加油站,可以免费加满油. 下面一行P个数字表示加油站的点标. 再下面一个整数Q 下面Q行 u v 表示在u点有销售站,可以卖掉邮箱里的任意数量的油,每以单位v元. 问跑到终点能获得最多多少元. 先求个每个点的最大剩余油量 f[i], 再把边反向,求每个点距离终点的最短路 dis[i]. 然后枚举一下每个销售点即可,( f[i] - dis

【最短路】【spfa】hdu6071 Lazy Running

给你一个4个点的环,问你从2号点出发, 再回到2号点,长度>=K的最短路是多少.环上的边长度不超过30000. 跑出来所有dis(2,j)以后,然后for一遍j,根据dis(2,j)+t*2*w>=K,解出来对于每个j而言最小的t,然后尝试更新答案即可.如果dis(2,j)已经大于等于K了,那直接用其尝试更新答案. 跟CDOJ1633很像. #include<cstdio> #include<queue> #include<algorithm> #inclu