题目描述
传送门
题解
先考虑如何得到最小的费用。
在原图的基础上加边。
x->y 容量为c,费用为d.
如果是扩容的话x->y 容量为inf,费用为b+d
如果是压缩的话y->x 容量为c,费用为a-d
这个图上跑最小费用最大流一定会得到最小费用,因为与起点相邻的边不做改变所以,最终的总流量也不变。
考虑最大化
X−Yk
,设新加入的边得到的总费用为
∑wi
,总流量为
∑limi
用01分数规划,将上面的式子变形。
X−Yk>=z
X−(X+∑wi)>=k∗z
−∑wi>=∑limi∗z
∑wi+∑limi∗z<=0
根据消圈定理:某个流f是同流量中的最小费用流,当且仅当f的残余网络中不存在负圈
我们把流量改成
wi+mid
,用spfa判断负环即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 30013
#define eps 1e-5
#define inf 1000000000
using namespace std;
double len[N],dis[N];
int cnt[N],can[N],nxt[N],point[N],v[N],tot,n,m;
bool pd;
void add(int x,int y,double z)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; len[tot]=z;
}
void spfa(int x,double mid)
{
can[x]=1;
for (int i=point[x];i;i=nxt[i])
if (dis[v[i]]>dis[x]+len[i]+mid) {
dis[v[i]]=dis[x]+len[i]+mid;
if (can[v[i]]==1) {
pd=true;
return;
}
spfa(v[i],mid);
if (pd) return;
}
can[x]=0;
}
bool check(double x)
{
pd=false;
memset(can,0,sizeof(can));
memset(dis,0,sizeof(dis));
for (int i=1;i<=n+2;i++) {
spfa(i,x);
if (pd) return true;
}
return false;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
double l=0,r=0;
for (int i=1;i<=m;i++) {
int x,y; double a,b,c,d;
scanf("%d%d%lf%lf%lf%lf",&x,&y,&a,&b,&c,&d);
r+=c*d;
if (x==n+1) continue;
add(x,y,b+d);
if (c) add(y,x,a-d);
}
double ans=0;
while (r-l>eps){
double mid=(l+r)/2;
if (check(mid)) ans=max(ans,mid),l=mid+eps;
else r=mid-eps;
}
printf("%.2lf\n",ans);
}