Description
四川的方伯伯为了致富,决定引进海南的椰子树。
方伯伯的椰子园十分现代化,椰子园中有一套独特的交通系统。
现在用点来表示交通节点,边来表示道路。这样,方伯伯的椰子园就可以看作一个有 n+2 个交通节点,m 条边的有向无环国。n+1 号点为入口,n+2号点为出口。每条道路都有 6 个参数:ui ,vi ,ai ,bi ,ci ,di ,分别表示,该道路从 ui 号点通向 vi 号点,将它的容量压缩一次要 ai 的花费,容量扩大一次要 bi 的花费,该条道路当前的运输容量上限为 ci,并且每单位运输量通过该道路要 di 费用。
在这个交通网络中,只有一条道路与起点相连。因为弄坏了这条道路就会导致整个交通网络瘫痪,聪明的方伯伯决定绝不对这条道路进行调整,也就是说,现在除了这条道路之外,对其余道路都可以进行调整。
有两种调整方式:
1. 选择一条道路,将其进行一次压缩,这条道路的容量会下降 1 单位。
2. 选择一条道路,将其进行一次扩容,这条道路的容量会上升 1 单位。
一条道路可以被多次调整。
由于很久以前,方伯伯就请过一个工程师,对这个交通网络进行过一次大的优化调整。所以现在所有的道路都被完全的利用起来了,即每条道路的负荷都是满的(每条道路的流量等于其容星)。
但方伯伯一想到自己的海南椰子会大丰收,就十分担心巨大的运输量下,会导致过多的花费。
因此,方伯伯决定至少进行一次调整,调整之后,必须要保持每条道路满负荷,且总交通量不会减少。
设调整后的总费用是 Y,调整之前的总费用是 X 。现在方伯伯想知道,最优调整比率是多少,即假设他进行了 k 次调整,(X-Y)/K 最大能是多少?
注:总费用 = 交通网络的运输花费 + 调整的花费。
Input
第一行包含二个整数N,M
接下来M行代表M条边,表示这个交通网络
每行六个整数,表示Ui,Vi,Ai,Bi,Ci,Di
接下来一行包含一条边,表示连接起点的边
Output
一个浮点数,保留二位小数。表示答案,数据保证答案大于0
Sample Input
5 10
1 5 13 13 0 412
2 5 30 18 396 148
1 5 33 31 0 39
4 5 22 4 0 786
4 5 13 32 0 561
4 5 3 48 0 460
2 5 32 47 604 258
5 7 44 37 75 164
5 7 34 50 925 441
6 2 26 38 1000 22
Sample Output
103.00
HINT
1<=N<=5000
0<=M<=3000
1<=Ui,Vi<=N+2
0<=Ai,Bi<=500
0<=Ci<=10000
0<=Di<=1000
解题思路:
即是要找一种使平均花费减少最多的流量转移方案。
注意到每改变1的流量,在保证答案最优的情况下代价都是相同的,所以转移多少并不是关键,只讨论单位流量即可。
考虑建图,对于原题中的边(u,v),都可以增加流量,所以从u向v连权值为b+d的边;对于本就有流量的边,可以退流,所以从v向u连权值为a-d的边。
现在只要新图中有一个负环,就有一种合法的转移方案,而一个大小为k的环需要调整k次,所以要求的即是平均权值最小的负环,这就是典型的分数规划题目了,二分+SPFA判负环即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=505,M=10005;
const double eps=1e-4;
int n,m,S,T,cnt[N];
int tot,first[N],nxt[M],to[M];
double w[M],dis[N];
bool exist[N];
queue<int>q;
void add(int x,int y,double z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
bool SPFA()
{
for(int i=1;i<=n;i++)exist[i]=0,cnt[i]=0,dis[i]=1e20;
while(!q.empty())q.pop();
q.push(S),exist[S]=1,dis[S]=0;
while(!q.empty())
{
int u=q.front();
q.pop(),exist[u]=0,cnt[u]++;
if(cnt[u]>n)return true;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(dis[v]>dis[u]+w[e])
{
dis[v]=dis[u]+w[e];
if(!exist[v])q.push(v),exist[v]=1;
}
}
}
return false;
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();S=n+1,T=n+2,n+=2;
while(m--)
{
int x=getint(),y=getint(),a=getint(),b=getint(),c=getint(),d=getint();
add(x,y,(double)b+d);if(c)add(y,x,(double)a-d);
}
double l=0,r=1000;
while(abs(r-l)>eps)
{
double mid=(l+r)/2;
for(int i=1;i<=tot;i++)w[i]+=mid;
if(SPFA())l=mid;
else r=mid;
for(int i=1;i<=tot;i++)w[i]-=mid;
}
printf("%0.2f",r);
return 0;
}