转载来源:優YoU http://user.qzone.qq.com/289065406/blog/1299337940
提示:关键在于反向利用Bellman-Ford算法
题目大意
有多种汇币,汇币之间可以交换,这需要手续费,当你用100A币交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到(100 - 0.39) * 29.75 = 2963.3975 B币。问s币的金额经过交换最终得到的s币金额数能否增加
货币的交换是可以重复多次的,所以我们需要找出是否存在正权回路,且最后得到的s金额是增加的
怎么找正权回路呢?(正权回路:在这一回路上,顶点的权值能不断增加即能一直进行松弛)
题目分析:
一种货币就是图上的一个点
一个“兑换点”就是图上两种货币之间的一个兑换环,相当于“兑换方式”M的个数,是双边
唯一值得注意的是权值,当拥有货币A的数量为V时,A到A的权值为K,即没有兑换
而A到B的权值为(V-Cab)*Rab
本题是“求最大路径”,之所以被归类为“求最小路径”是因为本题题恰恰与bellman-Ford算法的松弛条件相反,求的是能无限松弛的最大正权路径,但是依然能够利用bellman-Ford的思想去解题。
因此初始化d(S)=V 而源点到其他店的距离(权值)初始化为无穷小(0),当s到其他某点的距离能不断变大时,说明存在最大路径
#include<cstdio> #include<cstring> #include<queue> #include<cstdlib> #include<algorithm> #include<vector> #include<cmath> using namespace std; typedef long long LL; const int N=1e2+5; struct Edge{ int v,next; double r,c; }edge[N*2]; int n,m,s,head[N],tot,cnt[N]; bool inq[N]; double d[N],v; queue<int>q; void add(int u,int v,double r,double c){ edge[tot].v=v; edge[tot].r=r; edge[tot].c=c; edge[tot].next=head[u]; head[u]=tot++; } bool spfa(int s){ memset(d,0,sizeof(d)); memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); d[s]=v,q.push(s),inq[s]=true,cnt[s]=1; while(!q.empty()){ int u=q.front(); q.pop(); inq[u]=false; for(int i=head[u];~i;i=edge[i].next){ int to=edge[i].v; double r=edge[i].r,c=edge[i].c; if(d[to]<r*(d[u]-c)){ d[to]=r*(d[u]-c); if(!inq[to]){ inq[to]=true; if(++cnt[to]>n)return true; q.push(to); } } } if(d[s]>v)return true; } return false; } int main(){ scanf("%d%d%d%lf",&n,&m,&s,&v); memset(head,-1,sizeof(head)),tot=0; for(int i=0;i<m;++i){ int u,v; double r,c; scanf("%d%d%lf%lf",&u,&v,&r,&c); add(u,v,r,c); scanf("%lf%lf",&r,&c); add(v,u,r,c); } if(spfa(s))printf("YES "); else printf("NO "); return 0; }