题目:https://vjudge.net/problem/HDU-5988
题意:一个网络流的图,有n个点,从1~n,然后m条边,每个点有两个值,一个是人的数量一个是饭的数量。每条边有容量,还有走上去可能踩断电线的几率。问让所有人吃到饭的前提下断电线的最小概率是多少。
分析:使用log把乘法变加法,先建容量为1,费用为0的边,之后建正常的边,以0,n+1为源点和汇点跑费用流即可
代码:
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const double eps = 1e-9;
const int N = 1000 + 7 , M = 100000 + 7;
int head[N],nxt[M],to[M],from[M],cap[M];
double cost[M];
int d[N],pree[N],inq[N];
double dis[N];
int n,m,nEdge;
void AddEdge(int u,int v,int c,double co){
nxt[nEdge] = head[u];
to[nEdge] = v;
from[nEdge] = u;
cap[nEdge] = c;
cost[nEdge] = co;
head[u] = nEdge++;
}
double MIncostMaxFlow(int s,int t){
int flow = 0;
double res = 0;
while(1){
memset(dis,100,sizeof dis);
memset(inq,0,sizeof inq);
memset(d,0,sizeof d);
dis[s] = 0;
inq[s] = 1;
pree[s] = 0;
d[s] = inf;
queue<int>q;
q.push(s);
while(!q.empty()){
int u = q.front();q.pop();
inq[u] = 0;
for(int e = head[u];~e;e=nxt[e]){
int v = to[e];
if(cap[e]&&dis[v]>dis[u]+cost[e]+eps){
dis[v] = dis[u]+cost[e];
pree[v] = e;
d[v] = min(d[u],cap[e]);
if(!inq[v]){
q.push(v);
inq[v] = 1;
}
}
}
}
if(!d[t]) break;
flow+=d[t];
res+=d[t]*dis[t];
for(int u=t;u!=s;u=from[pree[u]]){
cap[pree[u]]-=d[t];
cap[pree[u]^1]+=d[t];
}
}
//cout<<res<<endl;
return res;
}
int main(){
freopen("i.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--){
memset(head,-1,sizeof head);
nEdge = 0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
int x = a-b;
if(x>0){
AddEdge(0,i,x,0);
AddEdge(i,0,0,0);
}
if(x<0){
AddEdge(i,n+1,-x,0);
AddEdge(n+1,i,0,0);
}
}
for(int i=1;i<=m;i++){
int u,v,c;
double co;
scanf("%d%d%d%lf",&u,&v,&c,&co);
if(c>0){
AddEdge(u,v,1,0);
AddEdge(v,u,0,0);
}
if(c-1>0){
AddEdge(u,v,c-1,-log(1.0-co));
AddEdge(v,u,0,log(1.0-co));
}
}
printf("%.2f\n",1-exp(-MIncostMaxFlow(0,n+1)));
}
}