题目链接
题目大意
有N(1 ≤ N ≤ 100)种货币(标号1到N),M种货币的两两兑换法则(包括汇率r和手续费c),具体的法则是:当你用100A币交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到(100 - 0.39) * 29.75 = 2963.3975 B币。现在一个人手头有S型号货币,量为V,问他能否经过交换最终得到S型号货币且数额增加(交易过程中货币不能为负)。
分析
一种货币看成图上的一个结点,一种兑换方式可以看成一条边
A到B的权值为(Va-Cab)*Rab,注意S到S的权值为V。
构图完毕后可以发现题目就是要判断图是否构成正权回路,所以可以逆用Bellman-Ford算法求最长路,用相反的松弛条件松弛N-1轮,如果还能继续松弛则存在正权回路。
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXM=210;
const int MAXN=110;
struct Edge
{
int beg;
int end;
double r,c;
};
Edge edge[MAXM];
int edgenum,n,m,s;
double dis[MAXN],val;
void Add_Edge(int beg,int end,double r,double c)
{
edgenum++;
edge[edgenum].beg=beg;//起点货币
edge[edgenum].end=end;//终点货币
edge[edgenum].r=r;//汇率
edge[edgenum].c=c;//手续费
}
bool Bellmen_Ford()
{
for (int i=1;i<=n;i++) dis[i]=0;//这里与bellman的目的刚好相反。初始化为源点到各点距离无穷小
dis[s]=val;//初始手里的货币量
for (int k=1;k<=n-1;k++)//进行N-1轮松弛
{
bool flag=false;
for (int i=1;i<=edgenum;i++)
{
int u=edge[i].beg;
int v=edge[i].end;
if (dis[v]<(dis[u]-edge[i].c)*edge[i].r)//寻找最长路径
{ 进行比较的是"某点到自身的权值"和"某点到另一点的权
flag=true;
dis[v]=(dis[u]-edge[i].c)*edge[i].r;
}
}
if (!flag) return false;
}
for (int i=1;i<=edgenum;i++)//正环能够无限松弛
if (dis[edge[i].end]<(dis[edge[i].beg]-edge[i].c)*edge[i].r)
return true;
return false;
}
int main()
{
int a,b;
double R_ab,C_ab,R_ba,C_ba;
while (scanf("%d%d%d%lf",&n,&m,&s,&val)!=EOF)
{
edgenum=0;
for (int i=1;i<=m;i++)
{
scanf("%d%d%lf%lf%lf%lf",&a,&b,&R_ab,&C_ab,&R_ba,&C_ba);
Add_Edge(a,b,R_ab,C_ab);
Add_Edge(b,a,R_ba,C_ba);
}
bool ans=Bellmen_Ford();
if (ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}