题意:
有nnn个物品,mmm组关系,每组关系以四个整数给出a,b,c,da,b,c,da,b,c,d,代表aaa个bbb物品可以换取ccc个ddd物品,现在存在某种关系,使得一个物品AAA可以通过反复置换其他物品,最后置换回AAA时数量大于1,为了平衡,决定找出一个常数w∈[0,1]w\in[0,1]w∈[0,1],使原本的关系变为
aaa个bbb物品可以换取kckckc个ddd物品,避免原本不平衡的情况出现。找出最大的符合条件的kkk。
方法:
首先,如果能通过置换其他物品置换回本身,那么这个图就一定存在环。我们把关系抽象成边,也就是b→db\rightarrow db→d,边权为ca\frac{c}{a}ac,也就是一个bbb能换ca\frac{c}{a}ac个ddd,我们只需要判断存不存在一个环,使得各边之积>1>1>1,就可以判断这个图是否符合条件。那么如何构造出新图,可以二分kkk,然后检查这个图,需要注意的是,边权变为原来的kkk倍。
如何判断图是否存在环使各边之积大于1呢?可以用bellman−fordbellman-fordbellman−ford算法,但是由于浮点数精度较差,并且www较小,考虑转化为对数,因为一个很小的整数的对数是一个很大的负数,因此可以转换为是否存在一个环,各边之和大于000,bellman−fordbellman-fordbellman−ford判负环反着来即可。
#include<bits/stdc++.h>
#define ll long long
#define fr first
#define se second
#define endl '\n'
using namespace std;
struct way
{
int from,to;
double w;
}edge[500005];
int n,m;
double dis[10005],eps=1e-6;
bool check(double k)
{
//返回是否没有正环
for(int i=1;i<=n;i++) dis[i]=0;
k=log(k);//每个边的长度加上一个k
int cnt=0;
while(1)
{
bool flag=false;
for(int i=1;i<=m;i++)
{
int u=edge[i].from,v=edge[i].to;
double w=edge[i].w;
if(dis[u]+w+k>dis[v])
{
flag=true;
dis[v]=dis[u]+w+k;
}
}
if(!flag) break;
cnt++;
if(cnt>n+10) return false;//出现环了
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,c,d;
cin>>a>>b>>c>>d;
edge[i].from=b;
edge[i].to=d;
edge[i].w=log(1.0*c/a);
}
double l=0,r=1,ans=0;
while(r-l>eps)
{
double mid=(l+r)/2;
if(check(mid)){
ans=max(ans,mid);
l=mid;
}
else r=mid;
}
printf("%.10lf",ans);
return 0;
}
也可以使用spfa判断,但使用spfa需要注意图可能不连通,所以每个点都需要入队
#include<bits/stdc++.h>
#define ll long long
#define fr first
#define se second
#define endl '\n'
using namespace std;
struct way
{
int to,next;
double w;
}edge[200005];
int cnt,head[100005];
void add(int u,int v,double w)
{
edge[++cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
int n,m;
queue<int>q;
bool in[100005];
int t[100005];
double dis[100005],eps=1e-6;
bool check(double k)
{
//是否有正环
k=log(k);
for(int i=1;i<=n;i++)
{
dis[i]=-172875082;
in[i]=false;t[i]=0;
}
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++)
{
q.push(i);in[i]=true;
dis[i]=0;t[i]++;
}
while(!q.empty())
{
int u=q.front();
q.pop();in[u]=false;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
double w=edge[i].w;
if(dis[u]+w+k>dis[v])
{
dis[v]=dis[u]+w+k;
if(!in[v])
{
q.push(v);in[v]=true;
if(++t[v]>=n) return true;
}
}
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b,c,d;
cin>>a>>b>>c>>d;
add(b,d,log(1.0*c/a));
}
double l=0,r=1,ans=0;
while(r-l>eps)
{
double mid=(l+r)/2;
if(!check(mid)){
ans=max(ans,mid);
l=mid;
}
else r=mid;
}
printf("%.10lf",ans);
return 0;
}