Description
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
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
题解:
假定点权为一个点的入流-出流,显然调整前后所有点权为0.
将一条边容量-1,等价于使起点点权+1,终点点权-1,并付出a-d的代价;将一条边容量+1,等价于使终点点权+1,起点点权-1,并付出b+d的代价。我们将每条边拆为两条边,分别表示扩容和压缩,每条边权值为代价,起点为点权+1的点。
由于最后所有点权是0,可知最后产生代价的边形成一个环。
如果这张图中有负环,沿着负环调整会使费用减小。设减小的费用是val,环上边数位k,考虑先二分一个答案mid,则需验证是否有val/k>=mid。将所有的边权加上mid,原问题等价于判是否有负环。
#include<bits/stdc++.h>
using namespace std;
const int N=505;
const int M=1e4+10;
int n,m,E;
int fir[N],nex[M],arr[M],vis[N];
double len[M],dis[N];
void Add_Edge(int x,int y,double l) {
nex[++E]=fir[x]; fir[x]=E;
arr[E]=y; len[E]=l;
}
int Spfa(int x) {
for (int i=fir[x];i;i=nex[i])
if (dis[arr[i]]>dis[x]+len[i]) {
if (vis[arr[i]]) return 0;
vis[arr[i]]=1;
dis[arr[i]]=dis[x]+len[i];
if (!Spfa(arr[i])) return 0;
}
vis[x]=0;
return 1;
}
int check(double mid) {
for (int i=1;i<=E;i++) len[i]+=mid;
for (int i=1;i<=n+2;i++) dis[i]=0x3f3f3f3f; dis[n+1]=0;
memset(vis,0,sizeof(vis)); vis[n+1]=1;
int t=!Spfa(n+1);
for (int i=1;i<=E;i++) len[i]-=mid;
return t;
}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=m+1;i++) {
int x,y,a,b,c,d; scanf("%d%d%d%d%d%d",&x,&y,&a,&b,&c,&d);
Add_Edge(x,y,b+d);
if (c) Add_Edge(y,x,a-d);
}
double l=0,r=0x3f3f3f3f;
while (r-l>=1e-4) {
double mid=(l+r)/2;
if (check(mid)) l=mid;
else r=mid;
}
printf("%.2f",l);
return 0;
}