主要基本与Floyd,Bellman-ford,SPFA 和 Dijkstra三种方法进行做题,以例题来说明。
这里给出四种算法大致对比 https://blog.csdn.net/v_JULY_v/article/details/6181485
下面进入到今天的题目
POJ 1860 Currency Exchange http://poj.org/problem?id=1860
题目描述的概括 :
有N货币,有M个能把a、b两种货币之相互兑换的点,对每个M点,给出6个数据分别a,b货币,a兑换成b的汇率与拥金,b兑换成a的汇率与拥金。问Nike能不能经过一些兑换操作后,使原金额V增加。例如,如果你想换100美元到俄罗斯卢布兑换点,那里的汇率是29.75,而佣金是0.39,你会得到(100 - 0.39)×29.75=2963.3975卢布。
这里我们可以先把题目抽象为图,把N个货币看成N个结点,M为2 * M条边,其中每2条边分别对应了2个结点,a -> b 和 b -> a;权值就是兑换后的货币数即当拥有货币A的数量为dis[A]时,dis[B] = (dis[A] - Cab) * Rab (这里有个关键是,这里是边权不是简单是求加权,而是x不断变化的条件下,dis[B]随着dis[A]变化而再次变化)。到这里我们的图就大致建好了。
回到问题上来,如何判断金额有无增加呢?从题目中我们可以看到一个重要条件,就是最后判断金额是要回到初始货币类型的,也就是说,我们走的是一条回路(回到起点)!这时候我们可以想到最短路径中有判断负圈的说法,那么我们能不能反思向维,既然我们要求的是最大金额,那么是最大路径判断正圈。
最后得出结论 :从s出发的 “最短路径” 中如果出现了 “负圈”(其实是最大路径出现正圈),那么金额就可以在通过一些兑换后增加。(SPFA, Bellman-ford, Floyd均可实现)。
代码段(以SPFA写法为例)
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define el '\n'
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define lop(i, a, b) for(int i = (a); i < (b); i++)
#define dwn(i, a, b) for(int i = (a); i >= (b); i--)
#define ms(a, b) memset(a, b, sizeof(a))
const int MAXN = 200;
bool vis[MAXN]; //记录有无入队列情况
double dis[MAXN]; //到起点的最大距离,初始都为0
int num[MAXN]; //进入队列次数
int n, m, s; //n个结点,m条边,初始点为s
double v; //初始货币数
struct edge{
int from, to;
double R, C;
}temp;
vector<edge> e[MAXN];
bool spfa(int star){//用spfa求最大路径判断正圈
queue<int> Q;
Q.push(star);
vis[star] = true;
dis[star] = v;
num[star]++;
while(!Q.empty()){
int u = Q.front();
Q.pop();
vis[u] = false;
lop(i, 0, e[u].size()){
int next = e[u][i].to;
if(dis[next] < (dis[u] - e[u][i].C) * e[u][i].R){
dis[next] = (dis[u] - e[u][i].C) * e[u][i].R;
if(!vis[next]){
Q.push(next);
vis[next] = true;
num[next]++;
if(num[next] > n)//当一个点进队次数超过n次,说明已经无限循环了,有正圈
return true;
}
}
}
}
return false;
}
int main(){
cin >> n >> m >> s >> v;
rep(i, 1, m){//输入
int a, b;
double r1, r2, c1, c2;
cin >> a >> b >> r1 >> c1 >> r2 >> c2;
temp.to = b, temp.R = r1, temp.C = c1;
e[a].push_back(temp);
temp.to = a, temp.R = r2, temp.C = c2;
e[b].push_back(temp);
}
if(spfa(s))
cout << "YES\n";
else
cout << "NO\n";
return 0;
}
补充一个差分约束的思想(博主对这个理解不是很透彻,所以借用一下大佬们的解释)
https://www.cnblogs.com/zzz-hhh/p/11200893.htmlhttps://www.cnblogs.com/zzz-hhh/p/11200893.html