题意:
给你n和m,有m组数据,每组是a,b,c,d,表示a个物品b可以转化为c个物品d,由于有可能经过这样转化的操作使得一些物品无限增加,为了防止这样的事情发生,求一个最大的w,让所有的c都乘上w,使这些物品不能无限增加。
根据题意很容易想到,应该要画个图,不妨让每个b和d连一条有向边,边权为(c/a),表示一个b可以生成(c/a)个d,这样为了符合题目要求,就是让这个图里面的每一个环,环里面所有边权值累乘的值≤1即可。
这样,就可以转化为这个环的权值和是否为负的情形了,即SPFA判负环。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
int a[N],c[N],n,m;
struct node
{
int u,v;
double w;
int nxt;
} edge[N];
int head[N],num=1;
void add(int x,int y,int z,int zz)
{
edge[num].u=x;
edge[num].v=y;
a[num]=z,c[num]=zz;
edge[num].nxt=head[x];
head[x]=num++;
}
double dis[N];
int vis[N];
int cnt[N];//判负环
typedef pair<int, int> pii;
bool spfa(double w)
{
for(int i=1;i<=n;i++)
{
dis[i]=1e8;
vis[i]=0;
dis[i]=0;
cnt[i]=0;
}
priority_queue<pii, vector<pii>, greater<pii>> q;
for(int j=1; j<=n; j++)
{
if(vis[j])continue;
int x=j;
dis[x]=0;
q.push(make_pair(0, x));
vis[j]=1;
while(!q.empty())
{
int xx=q.top().second;
q.pop();
for(int i=head[xx]; i; i=edge[i].nxt)
{
double ww=-log(w*c[i]/a[i]);
if(dis[edge[i].v]>dis[xx]+ww)
{
dis[edge[i].v]=dis[xx]+ww;
cnt[edge[i].v]=cnt[xx]+1;
if(cnt[edge[i].v]>=n)return false;
q.push(make_pair(dis[edge[i].v],edge[i].v));
}
}
}
}
return true;
}
signed main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
add(b,d,a,c);
}
double l=0,r=1;
while(r-l>1e-7)
{
double mid=(l+r)/2;
if(spfa(mid))
l=mid;
else r=mid;
//cout<<l<<" "<<r<<"\n";
}
printf("%.10lf",r);
return 0;
}