题意:任何两个国家的钱币都有汇率,当然也有手续费。如果A换B的汇率为C,手续费为D,当你用A换成B时,你最后可以得到(A-D)*C的B铅笔。询问你是否存在一种兑换方式,让你的钱币增加。
题解:这是一个找正环的的题,但是我们之前只讲过如何判断负环,但是实际我们会发现,这和之前的最短路的算法很类似,我们是需要在松弛条件上改一下大于小于号,就可以把最短路问题,就变成最长路,再考虑另外一个问题。我们如何判断正环,也就是经过一个环,权值变大。我们之前知道,如果一个图中如果不存在负环,那么最短路,最多是由n-2个点松弛出来的,也就是由n-1条遍构成的。借此思路,我们可以得到,最长路中正环的思路也是一样的。bellman-ford太慢,所以在这里用SPFA来解决此问题。
这里所有的正环是指经过这个环可以让钱变多的环。
具体看代码注释:
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 105;
int n,mark[N]; // mark表示该点是否在队列中
int visit[N],s; // 标记数组 用来记录,被松弛的几次
double dis[N],v; // dis记录 到i点的最长路
struct node{
int f; // 表示i到j之间能兑换
double c,r; // c表示 手续费,r表示兑换率
}map[N][N];
int spfa(){
int i,cur;
queue<int> q;
memset(mark,0,sizeof(mark)); // 清空很重要
memset(visit,0,sizeof(visit));
memset(dis,0,sizeof(dis));
mark[s] = 1; // 起始点入队
visit[s] = 1; // 标记起始点
dis[s] = v; // 起始点最开始一定是自己
q.push(s);
while(!q.empty()){
cur = q.front();
mark[cur] = 0;
q.pop();
for(int i = 1; i <= n; i ++){
if(map[cur][i].f){ // 如果有兑换方法
if(dis[i] <(dis[cur] - map[cur][i].c)*map[cur][i].r){ // 那么判断能不能松弛
dis[i] = (dis[cur] - map[cur][i].c)*map[cur][i].r;
if(!mark[i]){ // 如果没在队列中
mark[i] = 1; // 标记
visit[i]++; // 记录i被松弛了一次
if(visit[i] > n) // 若某个点被松弛n次以上,存在正环
return 1;
q.push(i); // 添加队列
}
}
}
}
}
return 0;
}
int main(){
int m,a,b;
double c,d,e,f;
while(~scanf("%d%d%d%lf",&n,&m,&s,&v)) {
memset(map,0,sizeof(map));
while(m--){
cin >> a >> b >> c >> d >> e >> f; //存图
map[a][b].f = 1;
map[a][b].r = c;
map[a][b].c = d;
map[b][a].f = 1;
map[b][a].r = e;
map[b][a].c = f;
}
if(spfa())
cout << "YES" << endl;
else
cout << "NO" << endl;
}
}