题意:给定n个点,m条有向边,每个点是一个吃饭的地方,每个人一盒饭。每个点有S个人,有B盒饭。每条边只能被走c次,每条边上都有电线,第一个人通过的时候,不会破坏电线,从第二个人开始,每次都有概率p破坏掉电线。使得每个人都能吃饭,求最小破坏电线的概率。
解法:每条边有走的次数(流量),每条边走一次发生破坏概率为p(流量1,费用p),容易想到费用流。可是费用流往往是费用相加的,这个是概率,只能相乘。有什么办法,log函数可以把乘除法转换为加减法。所以对每个概率取个log当成费用就行了。
log取底数取个2,然后对每条边的概率值取个对数,跑一次最小费用流,感觉没什么问题,但是会wa,因为概率总是小于1的,而底数是2,这样取log后会变为负数。费用为负,跑出来的费用就会朝更小走,在这个题上会出问题。那么取个负呢,把负变成正,还是会出问题,取负之后最小就变成了最大,跑出来是最大费用,也是会出问题的。
这时候就应该从反方向进行考虑,求踩坏的最小概率,就是求不踩坏的最大概率,1-p后取log,和以上同理,求出了最大费用。取出来还回去后用1减一下就好了。
新建源点s,汇点t,对于S>B的需要人走,从源点连一条流量为S[i]-B[i],费用为0(出门不需要费用)的边过去,add(s,i,S[i]-B[i],0),对于s<b的,add(i,t,B[i]-S[i],0)。
然后还有一个问题,就是第一次踩的时候,不会触发,那么从原有的边中取一条出来,流量1,费用0就好了。
------------------------------------------***********************************-----------------------------
然而我的模板很是垃圾,一个点到另一个点不能有多条边,然后GG,所以找了别人能过得一份代码当作我的模板
#include<cstdio>
#include<string.h>
#include<queue>
#include<algorithm>
#define maxm 50000
#define inf 1e8
using namespace std;
const int maxn = 200;
const double eps = 1e-7;
const float EXP = exp(1.0);
int num, p[maxn]; ///邻接表头结点
struct EDGE
{
int u, v, flow, next;
double cost;
EDGE() {}
EDGE(int u, int v, int flow,double cost, int next): u(u), v(v), flow(flow),cost(cost), next(next) {}
} E[maxm];
int n,m;
int S[maxn],B[maxn]; ///每个点的人数S,饭B
void init() ///初始化
{
num = 0;
memset(p, -1, sizeof p);
}
void add(int u, int v, int flow, double cost)
{
E[num] = EDGE(u, v, flow, cost, p[u]);
p[u] = num++;
E[num] = EDGE(v, u, 0, -cost, p[v]);
p[v] = num++;
}
int pre[maxn], mi[maxn];
double dis[maxn];
bool inq[maxn];
int s, t; ///源点汇点
double ans;
int flow;
int que[maxn];
bool spfa()
{
for(int i = 0; i <=t; i++)
inq[i] = 0, dis[i] = inf,pre[i]=-1;
dis[s] = 0, mi[s] = inf, inq[s] = 1;
int l = 0, r = 1;
que[l] = s;
while(l != r)
{
int u = que[l++];
if(l == maxn) l =0;
inq[u] = 0;
for(int i = p[u]; i + 1; i = E[i].next)
{
int v = E[i].v;
if(E[i].flow && dis[v] > dis[u] + E[i].cost+eps)
{
dis[v] = dis[u] + E[i].cost;
pre[v] = i;
mi[v] = min(mi[u], E[i].flow);
if(!inq[v])
{
inq[v] = 1;
que[r++] = v;
if(r >= maxn) r -= maxn;
}
}
}
}
if(pre[t]==-1) return false;
flow += mi[t];
ans += mi[t] * dis[t];
int u = t;
for(int i = pre[u]; i + 1; i = pre[E[i].u])
{
E[i].flow -= mi[t];
E[i ^ 1].flow += mi[t];
}
return true;
}
double Mincost()
{
ans = 0, flow = 0;
while(spfa());
return ans;
}
int main(void)
{
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&n,&m);
s = 105,t = 106;
for(int i = 1;i <= n;i++){
scanf("%d%d",&S[i],&B[i]);
if(S[i] > B[i]){ ///人数大于饭数,源点建边出来
add(s,i,S[i]-B[i],0);
}
if(B[i] > S[i]){ ///人数小于饭数,建边到汇点
add(i,t,B[i]-S[i],0);
}
}
for(int i = 1;i <= m;i++){
int u,v,f;
double pp;
scanf("%d%d%d%lf",&u,&v,&f,&pp);
add(u,v,1,0); ///第一次走没有费用
add(u,v,f-1,-log2(1-pp)); ///流量-1
}
double tmp = Mincost();
printf("%.2f\n",1-pow(2,-tmp));
}
return 0;
}